Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fragments being replaced while AsyncTask is executed - NullPointerException on getActivity()

Tags:

I recently converted my Activities to Fragments.

Using something similar to Tab-Navigation the fragments are replaced when the user selects another tab. After the fragment is populated I start at least one AsyncTask to get some information from the internet. However - if the user switches to another tab just as the doBackground-method from my AsyncTask is being executed - the fragment is replaced and thus I am getting a NullPointerException in the marked lines:

@Override
protected Object doInBackground(Object... params) {
  ...
  String tempjson = helper.SendPost(getResources().getText(R.string.apiid)); //ERROR: Fragment not attached
  ...
}

protected onPostExecute(Object result) {
  ...
  getActivity().getContentResolver() //NULLPOINTEREXCEPTION
  getView().findViewById(R.id.button) //NULL
  ...
}

getActivity() and getResources() causes an error because my Fragment is replaced.

Things I've tried:

  • Calling cancel method on my AsyncTask (won't fix first error nor the second error if the fragment is replaced while onPostExecute() is executed)
  • checking if getActivity() is null or calling this.isDetached() (not a real fix and I'd need to check it whenever I call getActivity() and so on)

So my question is: what would be the best to get rid of these AsyncTask problems? I did not have these problems using Activities as they weren't "killed" / detached on tab change (which resulted in higher memory usage - the reason why I like to switch to Fragments)

like image 377
Boni2k Avatar asked Nov 27 '11 21:11

Boni2k


2 Answers

Since AsyncTask is running in the background, your fragment may become detached from its parent activity by the time it finishes. As you've found out, you can use isDetached() to check. There's nothing wrong with that, and you don't have to check every time, just consider the fragment and activity life cycles.

Two other alternatives:

  • Use Loaders, they are designed to play nicer with fragments
  • Move your AsyncTask loading to the parent activity and use interfaces to decouple from the fragments. The activity would know whether a fragment is there or not, and act accordingly (by possibly discarding the result if the fragment is gone).
like image 87
Nikolay Elenkov Avatar answered Sep 28 '22 19:09

Nikolay Elenkov


Today I've faced the same problem: when I changed the fragment being displayed if the AsyncTask has not finished yet, and it tries to access the viewto populate it with some more elements, it would return a NullPointerException.

I solved the problem overriding one method of the fragments lifecycle: onDetach(). This method is called in the moment before the fragment is detached from the activity.

What you need to do is to call the cancel() method on your AsyncTask. This will stop the task execution avoid the NullPointerExecption.

Here's a sample of onDetach():

@Override
public void onDetach() {
    super.onDetach();
    task.cancel(true);
}

Check this page to get more information about fragments lifecycle: http://developer.android.com/reference/android/app/Fragment.html#Lifecycle And this to view more about Cancelling a task: http://developer.android.com/reference/android/os/AsyncTask.html

like image 43
Paradela Avatar answered Sep 28 '22 17:09

Paradela