赞
踩
流程介绍:
1.首先入口Handler类中runOnUiThread方法,如果当前线程不是UI线程,就调用mHandler.post(action);
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
2.实际post中会调用sendMessageDelayed(getPostMessage®, 0);方法
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
3.那么,sendMessageDelayed中是怎样已知Runnable拿到消息的呢?
看一下Handler类中getPostMessage方法,主要还是将r赋值给m.callback,并将消息返回
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
4.拿到消息以后就可以调用发送延迟消息的方法sendMessageDelayed(@NonNull Message msg, long delayMillis)
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
5.继续调用发送消息的方法sendMessageAtTime,返回时调用enqueueMessage方法将消息加入消息队列
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
6.把当前的handler作为msg的target属性。最终会调用queue的enqueueMessage的方法,也就是说handler发出的消息,最终会保存到消息队列中去。
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;这里就把handler的对象赋值给了msg
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以理解为在当前操作视图UI线程添加队列,实际上底层还是通过Handler从子线程切换到主线程,来实现UI的更新
先看一下view.post(Runnable)的源码实现
//=================View.java==========--
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);//①
}
// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().post(action);//②
return true;
}
上述源码的执行逻辑,关键点在mAttachInfo是否为null,这会导致两种逻辑:
1)mAttachInfo != null,走代码①的逻辑。
2)mAttachInfo == null,走代码②的逻辑。
当前View尚未attach到Window时,整个View体系还没有加载完,mAttachInfo就会为null,表现在Activity中,就是onResume()方法还没有执行完。反之,mAttachInfo就不会为null。
(1)mAttachInfo != null的情况
对于第一种情况,当看到代码①时,应该会窃喜一下,因为看到了老熟人Handler,这就是Handler.post(Runnable)方法,我们再熟悉不过了。这里的Runnable会在哪个线程执行,取决于该Handler实例化时使用的哪个线程的Looper。
在post(Runnable action)方法里,View获得当前线程(即UI线程)的Handler,然后将action对象post到Handler里。在Handler里,它将传递过来的action对象包装成一个Message(Message的callback为action),然后将其投入UI线程的消息循环中。在Handler再次处理该Message时,直接调用runnable的run方法。而此时,已经路由到UI线程里,因此,我们可以毫无顾虑的来更新UI。
我们看一下Handler再次处理该Message的过程:
首先Looper.loop方法中有一行Message msg = queue.next(); 取到消息,接着,会调用msg.target.dispatchMessage(msg);来回调消息,msg.target就是handler,所以等价于handler.dispatchMessage(msg);拿到消息去分发消息,具体实现看一下分发消息的源码:
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
我们看前两行,首先看一下msg.callBack是什么,它就是view.post中传入的runnable,当msg.callback != null的时候会执行 handleCallback(msg);我们看一下 handleCallback(msg)的源码实现:
private static void handleCallback(Message message) {
message.callback.run();
}
message.callback.run();就相当于执行runnable.run()
(2)mAttachInfo == null的情况
post源码中代码②有说明:推迟Runnable,直到我们知道需要它在哪个线程中运行。代码②处,看看getRunQueue()的源码:
/**
* Returns the queue of runnable for this view.
* @return the queue of runnables for this view
*/
private HandlerActionQueue getRunQueue() {
if (mRunQueue == null) {
mRunQueue = new HandlerActionQueue();
}
return mRunQueue;
}
getRunQueue()是一个单例模式,返回HandlerActionQueue实例mRunQueue。mRunQueue,顾名思义,表示该view的HandlerAction队列,下面会讲到,HandlerAction就是对Runnable的封装,所以实际就是一个Runnable的队列。注释中也提到,它用于推迟post的调用,直到该view被附着到Window并且拥有了一个handler。
代码②处执行的结果就是将post的参数Runnable action添加到View的全局变量mRunQueue中了,这样就将Runnable任务存储下来了。那么这些Runnable在什么时候开始执行呢?我们在View类中搜索一下会发现,mRunQueue的真正使用只有一处:
//===========View.java============
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
......
// Transfer all pending runnables.
if (mRunQueue != null) {
mRunQueue.executeActions(info.mHandler);
mRunQueue = null;
}
......
onAttachedToWindow();
......
}
到dispatchAttachedToWindow()方法了,,第一种情况也是到了这个方法就停下来了。
我们看看mRunQueue.executeActions(info.mHandler);这一行
传递的参数也是形参AttachInfo info的mHandler。进入到HandlerActionQueue类的executeActions可以看到,这个方法的作用就是通过传进来的Handler,来post掉mRunQueue中存储的所有Runnable,该方法中的逻辑就不多说了,比较简单。这些Runnable最终在哪个线程运行,就看这个Handler了。
到这里为止,两种情况就殊途同归了,最后落脚点都集中到了dispatchAttachedToWindow方法的AttachInfo参数的mHandler属性了。所以现在的任务就是找到哪里调用了这个方法,mHandler到底是使用的哪个线程的Looper。
mHandler所用Looper所在线程问题,其实就是伴随着启动Activity并绘制整个View的过程,可以得到如下简略流程图:
通过这里的dispatchAttachedToWindow方法,就将mHandler传递到了View.post()这个流程中,从而实现了从子线程中切换到主线程更新UI的功能。
延时而已,同post
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。