注册

Android View post 方法

解析View.post方法。分析一下这个方法的流程。


说起post方法,我们很容易联想到Handlerpost方法,都是接收一个Runnable对象。那么这两个方法有啥不同呢?


Handler的post方法


先来简单看一下Handlerpost(Runnable)方法。这个方法是将一个Runnable加到消息队列中,并且会在这个handler关联的线程里执行。


下面是关联的部分源码。可以看到传入的Runnable对象,装入Message后,被添加进了queue队列中。


Handler 有关的部分源码


    // android.os Handler 有关的部分源码
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}

private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

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);
}

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();

if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}

具体流程,可以看handler介绍


View的post方法


我们直接跟着post的源码走。


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;
}

private HandlerActionQueue getRunQueue() {
if (mRunQueue == null) {
mRunQueue = new HandlerActionQueue();
}
return mRunQueue;
}

可以看到一开始就查询是否有attachInfo,如果有,则用attachInfo.mHandler来执行这个任务。


如果没有attachInfo,则添加到View自己的mRunQueue中。确定运行的线程后,再执行任务。


post(Runnable action)的返回boolean值,如果为true,表示任务被添加到消息队列中了。
如果是false,通常表示消息队列关联的looper正在退出。


那么我们需要了解AttachInfoHandlerActionQueue


AttachInfo


AttachInfoView的静态内部类。View关联到父window后,用这个类来存储一些信息。


AttachInfo存储的一部分信息如下:



  • WindowId mWindowId window的标志
  • View mRootView 最顶部的view
  • Handler mHandler 这个handler可以用来处理任务

HandlerActionQueue


View还没有handler的时候,拿HandlerActionQueue来缓存任务。HandlerAction是它的静态内部类,存储Runnable与延时信息。


public class HandlerActionQueue {
private HandlerAction[] mActions;

public void post(Runnable action)
public void executeActions(Handler handler)
// ...

private static class HandlerAction {
final Runnable action;
final long delay;
// ...
}
}

View的mRunQueue


将任务(runnable)排成队。当View关联上窗口并且有handler后,再执行这些任务。


/**
* Queue of pending runnables. Used to postpone calls to post() until this
* view is attached and has a handler.
*/
private HandlerActionQueue mRunQueue;

这个mRunQueue里存储的任务啥时候被执行?我们关注dispatchAttachedToWindow方法。


void dispatchAttachedToWindow(AttachInfo info, int visibility) {
// ...
// Transfer all pending runnables.
if (mRunQueue != null) {
mRunQueue.executeActions(info.mHandler);
mRunQueue = null;
}
// ...
}

这个方法里调用了mRunQueue.executeActions


executeActions(Handler handler)方法实际上是用传入的handler处理队列中的任务。


而这个dispatchAttachedToWindow会被ViewGroup中被调用。


或者是ViewRootImpl中调用


host.dispatchAttachedToWindow(mAttachInfo, 0);

小结


View的post方法,实际上是使用了AttachInfohandler


如果View当前还没有AttachInfo,则把任务添加到了View自己的HandlerActionQueue队列中,然后在dispatchAttachedToWindow中把任务交给传入的AttachInfohandler。也可以这样认为,View.post用的就是handler.post


我们在获取View的宽高时,会利用View的post方法,就是等View真的关联到window再拿宽高信息。


流程图归纳如下


post-flow1.png


作者:rf_dev
链接:https://juejin.cn/post/7009652473937788964
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册