Android. Fragment Getactivity() Sometimes Returns Null

getActivity() returns null in Fragment function

commit schedules the transaction, i.e. it doesn't happen straightaway but is scheduled as work on the main thread the next time the main thread is ready.

I'd suggest adding an

onAttach(Activity activity)

method to your Fragment and putting a break point on it and seeing when it is called relative to your call to asd(). You'll see that it is called after the method where you make the call to asd() exits. The onAttach call is where the Fragment is attached to its activity and from this point getActivity() will return non-null (nb there is also an onDetach() call).

Android. Fragment getActivity() sometimes returns null

It seems that I found a solution to my problem.
Very good explanations are given here and here.
Here is my example:

pulic class MyActivity extends FragmentActivity{

private ViewPager pager;
private TitlePageIndicator indicator;
private TabsAdapter adapter;
private Bundle savedInstanceState;

@Override
public void onCreate(Bundle savedInstanceState) {

....
this.savedInstanceState = savedInstanceState;
pager = (ViewPager) findViewById(R.id.pager);;
indicator = (TitlePageIndicator) findViewById(R.id.indicator);
adapter = new TabsAdapter(getSupportFragmentManager(), false);

if (savedInstanceState == null){
adapter.addFragment(new FirstFragment());
adapter.addFragment(new SecondFragment());
}else{
Integer count = savedInstanceState.getInt("tabsCount");
String[] titles = savedInstanceState.getStringArray("titles");
for (int i = 0; i < count; i++){
adapter.addFragment(getFragment(i), titles[i]);
}
}


indicator.notifyDataSetChanged();
adapter.notifyDataSetChanged();

// push first task
FirstTask firstTask = new FirstTask(MyActivity.this);
// set first fragment as listener
firstTask.setTaskListener((TaskListener) getFragment(0));
firstTask.execute();

}

private Fragment getFragment(int position){
return savedInstanceState == null ? adapter.getItem(position) : getSupportFragmentManager().findFragmentByTag(getFragmentTag(position));
}

private String getFragmentTag(int position) {
return "android:switcher:" + R.id.pager + ":" + position;
}

@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("tabsCount", adapter.getCount());
outState.putStringArray("titles", adapter.getTitles().toArray(new String[0]));
}

indicator.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
Fragment currentFragment = adapter.getItem(position);
((Taskable) currentFragment).executeTask();
}

@Override
public void onPageScrolled(int i, float v, int i1) {}

@Override
public void onPageScrollStateChanged(int i) {}
});

The main idea in this code is that, while running your application normally, you create new fragments and pass them to the adapter. When you are resuming your application fragment manager already has this fragment's instance and you need to get it from fragment manager and pass it to the adapter.

UPDATE

Also, it is a good practice when using fragments to check isAdded before getActivity() is called. This helps avoid a null pointer exception when the fragment is detached from the activity. For example, an activity could contain a fragment that pushes an async task. When the task is finished, the onTaskComplete listener is called.

@Override
public void onTaskComplete(List<Feed> result) {

progress.setVisibility(View.GONE);
progress.setIndeterminate(false);
list.setVisibility(View.VISIBLE);

if (isAdded()) {

adapter = new FeedAdapter(getActivity(), R.layout.feed_item, result);
list.setAdapter(adapter);
adapter.notifyDataSetChanged();
}

}

If we open the fragment, push a task, and then quickly press back to return to a previous activity, when the task is finished, it will try to access the activity in onPostExecute() by calling the getActivity() method. If the activity is already detached and this check is not there:

if (isAdded()) 

then the application crashes.

Fragment getActivity is always returning null from Async Task

Pass Activity to AsyncTask method

new Demo().execute(getActivity());

public class Demo extends AsyncTask<Activity,Void,Void>{
Activity activity=null;
@Override
protected Void doInBackground(Activity... params) {
activity=params[0]; //get the activity instance
//Do your task

return null;
}
}

Why getContext() in fragment sometimes returns null?

First of all, as you can see on this link, the method onCreateView() inside the fragment's lifecycle comes after onAttach(), so you should have already a context at that point. You may wonder, why does getContext() return null then? the problem lies on where you are creating your adapter:

App.getApiService().getLatestNews().enqueue(new Callback<LatestNews>() {
@Override
public void onResponse(Call<LatestNews> call, Response<LatestNews> response) {
if (response.isSuccessful() && response.body().isSuccessfull()){
adapter = new LastNewsRVAdapter(getContext(), response.body().getData(), sideBanner);
rvLatestNews.setAdapter(adapter);
tvSonkuKabar.setVisibility(View.VISIBLE);
}
}

@Override
public void onFailure(Call<LatestNews> call, Throwable t) {

}
});

Though you are specifying a callback in onCreateView(), that does not mean the code inside that callback will run at that point. It will run and create the adapter after the network call is done. With that in mind, your callback may run after that point in the lifecycle of the fragment. What I mean is that the user can enter that screen (fragment) and go to another fragment or return to the previous one before the network request finishes (and the callback runs). If that happens, then getContext() could return null if the user leaves the fragment (onDetach() may have been called).

Besides, you can have memory leaks also, in case the activity is destroyed before your network request finishes. So you have two issues there.

My suggestions to solve those issues are:

  1. in order to avoid the null pointer exception and the memory leak, you should cancel the network request when the onDestroyView() inside the fragment is being called (retrofit returns an object that can cancel the request: link).

  2. Another option that will prevent the null pointer exception is to move the creation of the adapter LastNewsRVAdapter outside the callback and keep a reference to it in the fragment. Then, use that reference inside the callback to update the content of the adapter: link

getActivity() From Fragment to Fragment returning null


 adapter.setOnItemClickListner(new DailyMenuFrag());

The new DailyMenuFrag() here is a new fragment and it is not attached to any activity and hence getActivity() returns null.

Looks like you should use

adapter.setOnItemClickListner(this);

instead to use the current DailyMenuFrag instance as item click listener.

getActivity keeps returning null after executing a thread for a fragment

The issue is quite difficult to solve because the UI code is entangled with the data loading logic. I think the best solution here would be to make those things independent. This will most likely solve the problem, or at least make it much easier to find and resolve. Also will improve the feature design in general.

Here is a short code snipit as requested, just to understand my idea. I won't write the whole thing though because this is beyond the question.

Create a state class, e.g. StudentState. The class will also provide a listener interface that will send updates to the fragment.

public class StudentState {
// You might want to store Student_User here instead
// I'm not sure how your data is currently represented.
private DataSnapshot dataSnapshot;
// Can also be a list of listeners if needed.
private StudentStateListener studentStateListener;

public void registerListener(StudentStateListener listener) {
studentStateListener = listener;
}

public void unregisterListener() {
studentStateListener = null;
}

...

public void setDataSnapshot(DataSnapshot dataSnapshot) {
this.dataSnapshot = dataSnapshot;
if (studentStateListener != null) {
studentStateListener.onDataSnapshotLoadFinished(dataSnapshot);
}
}

public interface StudentStateListener {
void onDataSnapshotLoadFinished(DataSnapshot dataSnapshot);
}
}

Implement it in your Fragment:

public class StudentFragment implements StudentStateListener {

// The state can be initialized in multiple ways.
// It can be a singleton, or stored in Application class, or
// defined in your base activity, etc. Up to you. Ideally
// should be injected via dependency injection if you use one, such as Dagger.
private StudentState state;

@Override
public void onCreate() {
// initialize state
// ...
}

@Override
public void onResume() {
state.registerListener(this);
// If the data has already been loaded you might also want the following.
// It's up to you.
if (state.getDataSnapshot() != null) {
showAllMarkers(state.getDataSnapshot());
}
}

@Override
public void onPause() {
state.unregisterListener();
}

...

@Override
public void onDataSnapshotLoadFinished(DataSnapshot dataSnapshot) {
if (!isAdded()) {
return;
}
showAllMarkers(snapshot);
}

public void updateStudents() {
// I'm not quite sure how to work with your API but basically the idea is to load a new data snapshot and store it in the state
// This should happen in background thread.
studentQuery.loadDataSnapshot(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot) {
state.setDataSnapshot(dataSnapshot);
}
});
}
}

I hope this helps.. Good luck!



Related Topics



Leave a reply



Submit