为什么AsyncTask的postExecute方法不会在UI线程中运行?

3

我有一个嵌套的内部类,它扩展了AsyncTask以在后台运行db查询。在post execute方法中,我正在调用父类的方法来更新视图,就像这样:

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);
  }
}

onCursorLoaded方法的样子如下:

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

大多数情况下,这个方法可以正常工作 - 但是有些用户会遇到以下崩溃问题:
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)

所以,为什么postExecute()方法没有在主线程上调用呢?

你如何定义继承AsynkTask的内部类?你能展示一下代码吗? - hovanessyan
请在顶部代码块中更新完整的AsycTask代码。 - harmanjd
我是指整个层次结构 - 父类 + 内部类定义。父类是一个Activity吗? - hovanessyan
父类扩展自兼容库中的FragmentActivity。 - harmanjd
@harmanjd 在你的 onPostExecute() 方法中使用 super.onPostExecute(null); - ρяσѕρєя K
2个回答

2

onPause() - onResume()循环不会经过onStart()。如果您需要在Activity启动时执行此操作,请将其放在onResume()中。您总是会通过onResume()。


我不明白这与我的问题有什么关系。 - harmanjd

2
我认为@hovanessyan的意思是,不能保证在AsyncTask启动时与之通信的Fragment实例与执行结束时相同。Android可能会在AsyncTask运行时暂停甚至终止Fragment。这就解释了问题为什么是间歇性的。它只发生在用户的Fragment已被暂停或销毁并重新创建时。
此问题在本文中有所讨论。如果您阅读文章中的第2和第3项,您将了解如何解决此问题。您需要负责保存和恢复Fragment生命周期的状态。请参见Fragment lifecycle的Android文档。

感谢提供文章链接。我认为我现在更好地理解了正在发生的事情 - 但让我在这里陈述一下以确保。在活动生命周期中:onCreate(),onStart(),onResume(),onPause(),onStop(),onDestroy()中,如果您在onCreate()或之后创建AsyncTask,并且通过onPause()暂停Activity,则当再次调用onResume()时,它将在主线程上运行,但不一定是用于最初创建活动的同一主线程。在这种情况下,当AsyncTask完成其onPostExecute()方法时,可能会出现此行为。 - harmanjd
通过使用文章中提到的模式,这种情况不会发生,因为当活动被暂停时,它会阻止回调直到活动恢复,在此时您正在运行新线程上下文。这是您的理解吗? - harmanjd
是的,那是我的理解。挺复杂的,对吧? - Chuck Krutsinger
是的,有点儿。无论如何 - 基于您在这里提供的链接提示,我改变了使用LoaderManagerCursorLoader的方法。 LoaderManager负责此生命周期并管理正确传递结果的时间,我避免了我在问题中描述的问题。 我以前没有这样做,因为对于这个特定的活动,如果数据库更改,我不想重新加载游标。 但是,我创建了CursorLoader的子类以避免注册内容观察器。 - harmanjd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接