更新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