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() 方法在主线程上调用,方法是向主线程的消息队列发布消息。

    李>

    配置更改的消息将包含调用ActivityFragment 生命周期方法的代码(例如onDetach()onAttach())。 AsyncTask 的消息将包含调用onPostExecute() 方法的代码。由于主线程按顺序处理其消息队列中的消息,因此两条消息不可能同时执行,因此在调用onDetach()onAttach() 之间永远不能调用onPostExecute()

阅读我在this thread 中对 Doug Stevenson 的回复,以获得更详细的解释(包括证明该主张的源代码链接)。

【讨论】:

在配置更改期间,是否保证onAttachonDetach 之后立即发布到消息队列? @DavidT。 onDetachonAttach 都由主 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 线程消息队列调度顺序的主要内容,如果未能解决你的问题,请参考以下文章

2017.12.18 Android开发之消息队列(实现子线程修改UI组件)

POSIX消息队列 - mq_send线程唤醒命令

消息循环是不是在 UI 线程上执行?

如何在android一条单独线程,更新ui ?

Android消息循环机制

Android中Handle详解