harmanjd
harmanjd

Reputation: 1954

Why would an AsyncTask's postExecute method not run in the UI thread?

I have a nested inner class that extends AsyncTask to run a db query in the background. In the post execute method I am calling a parent's method to update the view something like this

private class QueryRunner extends AsyncTask<Void,Void,Cursor> {

  @Override
  protected Cursor doInBackground(Void... voids)
  {
     return getContentResolver().query(LeadContentProvider.buildUri(app.getEntityId()),new String[]{LeadContentProvider._ID},null,null,LeadContentProvider.LEAD_STATUS_DATETIME +" desc");
  }

  @Override
  protected void onPostExecute(Cursor c)
  {
     onCursorLoaded(c);
  }
}

The onCursorLoaded method looks like:

private void onCursorLoaded(Cursor c)
{
  mPagerAdapter = new LeadDetailsFragmentPagerAdaper(getSupportFragmentManager());
  mPager.setAdapter(mPagerAdapter);
  mPager.setCurrentItem(iIndex, false);
}

Most of the time this works fine - but some users have crashes with this stack trace:

java.lang.IllegalStateException: Must be called from main thread of process
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1392)
at android.support.v4.app.FragmentManagerImpl.executePendingTransactions(FragmentManager.java:431)
at android.support.v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.java:139)
at android.support.v4.view.ViewPager.populate(ViewPager.java:804)
at android.support.v4.view.ViewPager.setAdapter(ViewPager.java:344)
at com.servicemagic.pros.activities.LeadDetails.onCursorLoaded(LeadDetails.java:205)
at com.servicemagic.pros.activities.LeadDetails.access$200(LeadDetails.java:25)
at com.servicemagic.pros.activities.LeadDetails$QueryRunner.onPostExecute(LeadDetails.java:196)
at com.servicemagic.pros.activities.LeadDetails$QueryRunner.onPostExecute(LeadDetails.java:170)
at android.os.AsyncTask.finish(AsyncTask.java:417)
at android.os.AsyncTask.access$300(AsyncTask.java:127)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:429)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:150)
at android.os.HandlerThread.run(HandlerThread.java:60)

So - why is the postExecute() method not called on the MainThread?

Upvotes: 3

Views: 3625

Answers (2)

Chuck Krutsinger
Chuck Krutsinger

Reputation: 2930

I believe what @hovanessyan is getting at is that it is not safe to assume that the Fragment your AsyncTask was talking to when it launched may not be the same instance as when it finishes executing. Android may pause or even terminate the Fragment while the AsyncTask is running. That would explain why the problem is intermittent. It is only happening to users when their Fragment has been paused or destroyed and recreated.

This issue is discussed in this article. If you read items 2 and 3 in the article you will see how to address it. You are responsible for saving and restoring state as part of your Fragment life cycle. See android documentation of Fragment lifecycle.

Upvotes: 2

hovanessyan
hovanessyan

Reputation: 31463

the onPause() - onResume() loop does not go through onStart(). If you need this to be executed on the start of your Activity, put it in onResume(). You always go through onResume().

Upvotes: 2

Related Questions