runOnUiThread() 和post()

更新UI是要主线程来更新的,即UI线程更新。如果在主线线程之外的线程中直接更新页面显示常会报错。抛出异常:android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

Activity.runOnUiThread(Runnable)把更新ui的代码创建在Runnable中,然后在需要更新ui时,把这个Runnable对象传给Activity.runOnUiThread(Runnable)。 这样Runnable对像就能在ui程序中被调用。如果当前线程是UI线程,那么行动是立即执行。如果当前线程不是UI线程,操作是发布到事件队列的UI线程

    public static void showToastSafe(final Activity activity, 
            final String text, final int duration) {
        // 方法1 activity.runOnUiThread
        activity.runOnUiThread(new Runnable() {
            
            @Override
            public void run() {
                Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
            }
        });
    }
 

handler.post

    public static void showToastSafe(final Context context, final String text) {
        // 方法2  handler.post  内部其实是做handler的流程  sendMsg之类的  功能是一样的  调用更简练
        new Handler().post(new Runnable() {
            
            @Override
            public void run() {
                Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
            }
        });
    }
 

imageview.postDelayed(new Runnable() {  

        @Override  
        public void run() {  
            Intent mIntent = new Intent(MainActivity.this,  
                    SecondActivity.class);  
            startActivity(mIntent);  
            finish();  

        }  
    }, 2000); 1
 

1,如果post方法是handler的,则Runnable执行在handler依附线程中,可能是主线程,也可能是其他线程。

2,如果post方法是View的,则一定是运行在主线程中的,因为所有view都自带一个handler,所有handler都有post方法,所以它的Runnable是运行在主线程中的

先从最简单的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。

抛开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执行之后执行。

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

参考资料:

view.post和Handler.post区别:http://blog.csdn.net/a740169405/article/details/69668957 

https://blog.csdn.net/johnlee175/article/details/52369173

猜你喜欢

转载自blog.csdn.net/kdsde/article/details/81507660
今日推荐