Android UI 线程消息队列调度顺序
Posted
技术标签:
【中文标题】Android UI 线程消息队列调度顺序【英文标题】:Android UI Thread Message Queue dispatch order 【发布时间】:2013-11-26 16:02:06 【问题描述】:虽然在 android 中使用 retain Fragments 在配置更改期间保存 AsyncTask,我想这是最好的方法,但我对 UI Thread 的消息队列调用顺序产生了一些疑问。
例如: 想象一下这种情况:
-
发生配置更改,用户旋转设备。 AsyncTask 正在运行。
片段
onDetach()
被调用
AsyncTask doInBackground()
方法完成
AsyncTask onPostExecute()
被调用
片段onAttach()
被调用
UI线程消息队列也可以这样:
队列顶部-> onDetach() | onPostExecute() | onAttach()
我知道它不能,就我所知,对onPostExecute()
的调用将等到配置更改完成,但它是如何工作的?来自活动、片段生命周期的调用是否连续执行?
【问题讨论】:
是的,由于onPostExecute()
在 UI 线程上运行,它会随着 UI 和配置更改连续执行,这些更改也在 UI 线程上运行。我的假设是 onDetach()
和 onAttach()
在某种程度上以原子方式运行,因为 ActivityManager 在配置更改期间可能不会屈服于其他线程,但这是您必须深入研究 Android 源代码才能确认的事情。
是的,查看 android 源代码可能是了解其工作原理的最佳方式。
@323go 进一步澄清,在这种情况下,Android 永远不必“屈服于其他线程”。 Android UI 工具包的单线程特性保证了原子性(即事件在一个集中的消息队列中按顺序执行)。
【参考方案1】:
在配置更改期间,不能在 Fragment#onDetach()
和 Fragment#onAttach()
之间调用 onPostExecute()
。这种说法背后的原因有三个:
配置更改在主线程的消息队列中的单个消息内处理。
只要doInBackground()
方法返回,AsyncTask
就会安排onPostExecute()
方法在主线程上调用,方法是向主线程的消息队列发布消息。
配置更改的消息将包含调用Activity
和Fragment
生命周期方法的代码(例如onDetach()
和onAttach()
)。 AsyncTask
的消息将包含调用onPostExecute()
方法的代码。由于主线程按顺序处理其消息队列中的消息,因此两条消息不可能同时执行,因此在调用onDetach()
和onAttach()
之间永远不能调用onPostExecute()
。
阅读我在this thread 中对 Doug Stevenson 的回复,以获得更详细的解释(包括证明该主张的源代码链接)。
【讨论】:
在配置更改期间,是否保证onAttach
在onDetach
之后立即发布到消息队列?
@DavidT。 onDetach
和 onAttach
都由主 UI 线程在同一消息中调用。
这不只是当前的实现细节,因此我们将依赖白盒行为吗?例如。这种行为以后不能改变吗?
@greg7gkb 你可能是对的......我觉得我记得读过这是故意的行为,将来不会从一些 Android 人那里破坏,但我忘记了这个记录在案。
我发现您提出的解决方案存在一些缺陷:***.com/questions/53603796/…【参考方案2】:
我编写了一个简单的测试来查看保留的Fragment
中关于AsyncTask
的生命周期。
它可以确认@Alex Lockwood 的回答是正确的。
因此可以肯定地说,保留的Fragment
中的AsyncTask
是最佳实践。而谷歌应该将这种方法写入他们的官方文档中。
public class RecordDataFragment extends Fragment
public static boolean detach = false;
@Override
public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setRetainInstance(true);
@Override
public void onAttach(Context context)
super.onAttach(context);
Timber.d("retain, onAttach");
detach = false;
@Override
public void onDetach()
super.onDetach();
Timber.d("retain, onDetach");
detach = true;
public static class TestTask extends AsyncTask<String, Void, Void>
protected Void doInBackground(String... username)
Timber.d("retain, looping.");
while(!detach)
try
Thread.sleep(1);
catch (InterruptedException e)
e.printStackTrace();
Timber.d("retain, exit looping.");
return null;
protected void onPostExecute(Void nothing)
Timber.d("retain, onPostExecute");
public class RecordFragment extends Fragment
static boolean called = false;
@Override
public void onResume()
super.onResume();
Timber.d("retain, onResume");
if(!called)
new RecordDataFragment.TestTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
called = true;
2019-11-22 12:28:55.503 D/RecordDataFragment: retain, onAttach
2019-11-22 12:32:00.263 D/RecordFragment: retain, onViewStateRestored
2019-11-22 12:32:03.538 D/RecordFragment: retain, onResume
2019-11-22 12:32:03.544 D/RecordDataFragment$TestTask: retain, looping.
2019-11-22 12:32:07.273 D/RecordDataFragment: retain, onDetach
2019-11-22 12:32:07.297 D/RecordDataFragment$TestTask: retain, exit looping.
2019-11-22 12:32:07.403 D/RecordFragment: retain, onDestroy
2019-11-22 12:32:07.566 D/RecordDataFragment: retain, onAttach
2019-11-22 12:32:08.621 D/RecordFragment: retain, onViewStateRestored
2019-11-22 12:32:08.870 D/RecordFragment: retain, onResume
2019-11-22 12:32:09.663 D/RecordDataFragment$TestTask: retain, onPostExecute
【讨论】:
以上是关于Android UI 线程消息队列调度顺序的主要内容,如果未能解决你的问题,请参考以下文章