Android从源码分析handler.post(runnable),view.post(runnable),runOnUiThread(runnable)执行时机

         大家好,我是听者,耳听心受的听,孙行者的者,感谢大家阅读我的文章。废话不说直接进入主题,不管是Android还是其他语言,线程之间通信都是一个比较“头疼”问题,开发Android的码农应该都知道回到主线程的方式有handler.post(runnable),view.post(runnable),runOnUiThread(runnable)。但是这三种方式的区别以及其执行的时机如何呢?今天就给大家剖析一下。

        下面大家先看一段代码:


private void test(View view,final String from) {
        view.post(new Runnable() {
            @Override
            public void run() {
                Log.e("MainActivity", from+"--->View" + System.currentTimeMillis());
            }
        });

        new Handler().post(new Runnable() {
            @Override
            public void run() {
                Log.e("MainActivity", from + "--->Handler" + System.currentTimeMillis());
            }
        });

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Log.e("MainActivity", from + "--->runOnUiThread" + System.currentTimeMillis());
            }
        });

    }


在这个方法里我们调用了三个post方法。当给方法在onResume之前调用时,其打印日志是:

06-21 14:41:19.255 7149-7149/com.duanchao.testapplication E/MainActivity: onResume--->runOnUiThread1466491279258

06-21 14:41:19.275 7149-7149/com.duanchao.testapplication E/MainActivity: onResume--->Handler1466491279277

06-21 14:41:19.335 7149-7149/com.duanchao.testapplication E/MainActivity: onResume--->View1466491279341

大家可以发现runOnUiThread时间明显比其他两个执行的早,而View.post()时间明显比其他两个执行的晚,大家可能会说程序执行是需要时间的,对,是这样的,但是需要这么长的时间吗?带着这个疑问咱们再看下一段日志:

06-21 15:10:51.185 28592-28592/com.duanchao.testapplication E/MainActivity: onCreate--->runOnUiThread1466493051194

06-21 15:10:51.195 28592-28592/com.duanchao.testapplication E/MainActivity: onResume--->runOnUiThread1466493051196

06-21 15:10:51.205 28592-28592/com.duanchao.testapplication E/MainActivity: onCreate--->Handler1466493051211

06-21 15:10:51.205 28592-28592/com.duanchao.testapplication E/MainActivity: onResume--->Handler1466493051212

06-21 15:10:51.255 28592-28592/com.duanchao.testapplication E/MainActivity: onCreate--->View1466493051264

06-21 15:10:51.255 28592-28592/com.duanchao.testapplication E/MainActivity: onResume--->View1466493051264

这段日志是在OnCreate和onResume里分别调用了test()方法,很明显OnCreate中的runOnUiThread、Handler().post(),view.post(),执行时间相隔还是很长,onResume也同样如此,但是runOnUiThread()在onResume和OnCreate两次调用的时间间隔很短,Handler().post(),view.post()也同样如此。那么问题就来了,这是为什么呢?其实大家都知道在Android中线程之间的交互的基础就是handler、looper、messageQueue,所以不论是runOnUiThread()还是view.post()最终还是使用了handler的基本原理。那么问题就又来了,既然原理相同调用时间也相同怎么在执行的时间上有这么大的差距呢?下面就给大家通过源码来分析一下这个问题。

下面咱们先从最简单的runOnUiThread()来看源码:

 public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }

这是runOnUiThread()方法的源码,从源码可以看到,当一个Runnable进来时,判断当前线程是否是主线程,如果是主线程直接调用其run()方法了,不是主线程则调用handler的post()回到了handler。不管是onResume还是OnCreate里调用runOnUiThread()只要是主线程中调用,其Runnable的run()方法立刻就执行。(结论一)

下面咱们再看一下view.post()的源码:

public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }
        // Assume that post will succeed later
        ViewRootImpl.getRunQueue().post(action);
        return true;
    }

它先判断了一个叫attachInfo是不是为null,如果不是null,调用了Handler的post()方法回到了handler,那么mAttachInfo是一个什么东东呢?下面我们再看一段代码:

/**
     * Returns true if this view is currently attached to a window.
     */
    public boolean isAttachedToWindow() {
        return mAttachInfo != null;
    }ViewRootImpl

如上mAttachInfo是用来判断是否attached到window的,这里留一个疑问即view什么时候attached到window上的呢?当为null的时候调用ViewRootImpl.getRunQueue().post(action),那么它的调用时机是什么呢?通过跟踪源码可以得到在ViewRootImpl的一系列方法中,比如draw(boolean)等。而ViewRootImpl.getRunQueue().post(action)的处理action是handler.postDelayed(handlerAction.action, handlerAction.delay)最后也回归到了handler。

下面再看一段日志:

06-21 18:28:54.055 25805-25805/com.duanchao.testapplication E/MainActivity: onCreate1466504934063

06-21 18:28:54.075 25805-25805/com.duanchao.testapplication E/MainActivity: onCreate--->runOnUiThread1466504934079

06-21 18:28:54.075 25805-25805/com.duanchao.testapplication E/MainActivity: onStart1466504934080

06-21 18:28:54.075 25805-25805/com.duanchao.testapplication E/MainActivity: onResume1466504934083

06-21 18:28:54.085 25805-25805/com.duanchao.testapplication E/MainActivity: onCreate--->Handler1466504934091

06-21 18:28:54.095 25805-25805/com.duanchao.testapplication E/TestView: onAttachedToWindow1466504934102

06-21 18:28:54.185 25805-25805/com.duanchao.testapplication E/MainActivity: onCreate--->View1466504934191

06-21 18:28:54.195 25805-25805/com.duanchao.testapplication E/TestView: onDraw1466504934204

抛开runOnUiThread不说因为上面已经说了该方法在主线程立刻会执行的,Handler().post()是在生命周期onResume之后执行的,而view.post()是在onAttachedWindow之后执行,也就是说ViewRootImpl.getRunQueue().post(action)是在onAttachedWindow之后执行的。由此我们得出一个结论,当程序启动一个activity时,OnCreate、onStart、onResume任务都添加到了主线程Looper的messageQueue中,在这个三个生命周期使用handler.post()都添加到messageQueue队列尾部,等待执行。而View.post(),最终也会添加到messageQueue队列中,等待onAttachedToWindow执行之后执行。(结论二)

下面我们再看一段日志:

06-21 18:42:09.985 17310-17310/com.duanchao.testapplication E/MainActivity: onClick--->runOnUiThread1466505729990

06-21 18:42:09.985 17310-17310/com.duanchao.testapplication E/MainActivity: onClick--->View1466505729990

06-21 18:42:09.985 17310-17310/com.duanchao.testapplication E/MainActivity: onClick--->Handler1466505729990

当在onResume之后点击某一button时打印的日志,由此看见当前面条件都满足时,在调用这三个post()方法时,都添加到messageQueue中,在每一个任务量小的时基本是同时执行的。那么我们就可以得出一个结论,在界面绘制成功以后再调用这三个方法时,当在子线程中调用时其效果是一样的,当在主线程中runOnUiThread是立刻执行该任务,而其他两个是加载到messageQueue队尾,当前面任务全部执行完毕再执行。(结论三)

下面我们再看一段终极日志来验证前面的结论:

06-21 18:54:19.975 7602-7602/com.duanchao.testapplication E/MainActivity: 前onClick--->runOnUiThread1466506459983

06-21 18:54:19.985 7602-7602/com.duanchao.testapplication E/MainActivity: 后onClick--->runOnUiThread1466506459993

06-21 18:54:19.985 7602-7602/com.duanchao.testapplication E/MainActivity: 前onClick--->View1466506459993

06-21 18:54:19.985 7602-7602/com.duanchao.testapplication E/MainActivity: 前onClick--->Handler1466506459993

06-21 18:54:19.985 7602-7602/com.duanchao.testapplication E/MainActivity: onPause1466506459994

06-21 18:54:19.995 7602-7602/com.duanchao.testapplication E/MainActivity: 后onClick--->View1466506460002

06-21 18:54:19.995 7602-7602/com.duanchao.testapplication E/MainActivity: 后onClick--->Handler1466506460002

06-21 18:54:20.005 7602-7602/com.duanchao.testapplication E/SecondActivity: onCreate1466506460007

06-21 18:54:20.005 7602-7602/com.duanchao.testapplication E/SecondActivity: onStart1466506460007

06-21 18:54:20.005 7602-7602/com.duanchao.testapplication E/SecondActivity: onResume1466506460009

06-21 18:54:20.385 7602-7602/com.duanchao.testapplication E/MainActivity: onStop1466506460391

06-21 18:57:09.725 7602-7602/com.duanchao.testapplication E/SecondActivity: onPause1466506629732

06-21 18:57:09.735 7602-7602/com.duanchao.testapplication E/MainActivity: onActivityResult1466506629742

06-21 18:57:09.735 7602-7602/com.duanchao.testapplication E/MainActivity: onActivityResult--->runOnUiThread1466506629742

06-21 18:57:09.735 7602-7602/com.duanchao.testapplication E/MainActivity: onStart1466506629742

06-21 18:57:09.735 7602-7602/com.duanchao.testapplication E/MainActivity: onResume1466506629743

06-21 18:57:09.755 7602-7602/com.duanchao.testapplication E/MainActivity: onActivityResult--->View1466506629757

06-21 18:57:09.755 7602-7602/com.duanchao.testapplication E/MainActivity: onActivityResult--->Handler1466506629757

06-21 18:57:10.095 7602-7602/com.duanchao.testapplication E/SecondActivity: onStop1466506630104

06-21 18:57:10.095 7602-7602/com.duanchao.testapplication E/SecondActivity: onDestroy1466506630104

onclick的代码如下:

view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                test(view,"前onClick");
                startActivityForResult(new Intent(MainActivity.this, SecondActivity.class), 100);
                test(view,"后onClick");
            }
        });

验证结论一:所有的runOnUiThread都在其调用方法中执行的。

结论二:在onclick中第一次调用test()时,将handler和View.post()的任务加载到了messageQueue的队列中,调用startActivityForResult时将MainActivity的stop任务添加到了队列中,当第二次调用test()时,又将handler和View.post()的任务加载到了messageQueue的队列中,然后启动secondActivity时,OnCreate、onStart、onResume都添加队列中。如上log可以验证改结论正确。

结论三:当Activity绘制成功以后,不论是在哪里执行handler().post()和view.post()是没有区别的。以上log验证正确。



你可能感兴趣的:(Android高级开发系列笔记,Android,线程)