一、View.post()
- post(Runnable action)
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
getRunQueue().post(action);
return true;
}
从上面代码可以知道,当调用post()方法时,首先会判断mAttachInfo是否为空,如果不为空,则调用Handler处理消息,否则,将将消息放入到RunQueue消息队列。
- HandlerActionQueue
View.java
private HandlerActionQueue getRunQueue() {
if (mRunQueue == null) {
mRunQueue = new HandlerActionQueue();
}
return mRunQueue;
}
HandlerActionQueue.java
private HandlerAction[] mActions;
public void post(Runnable action) {
postDelayed(action, 0);
}
public void postDelayed(Runnable action, long delayMillis) {
final HandlerAction handlerAction = new HandlerAction(action, delayMillis);
synchronized (this) {
if (mActions == null) {
mActions = new HandlerAction[4];
}
mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
mCount++;
}
}
从上面代码知道, getRunQueue()创建一个HandlerActionQueue,并将我们使用的runable放入到mActions内。
- 总结:
其实我们调用的post方法执行流程有两种,一种是直接使用Handler去执行,第二是将我们的runabale存储到队列中。
二、runable队列被执行
在HandlerActionQueue中我们知道,有一个executeActions(handler)方法。方法源码如下:
HandlerActionQueue.java
public void executeActions(Handler handler) {
synchronized (this) {
final HandlerAction[] actions = mActions;
for (int i = 0, count = mCount; i < count; i++) {
final HandlerAction handlerAction = actions[i];
handler.postDelayed(handlerAction.action, handlerAction.delay);
}
mActions = null;
mCount = 0;
}
}
从上面源码我们知道,executeActions内部通过for循环的方式,将消息队列的Runable取出,使用Handler发送到主线程。
三、HandlerActionQueue的executeActions方法合适被执行。
在View的源码中 Ctrl+F 我们搜索mRunQueue.executeActions。中我们找到只有在View的dispatchAttachedToWindow方法中执行。dispatchAttachedToWindow的核心代码如下:
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mAttachInfo = info;
...
if (mRunQueue != null) {
mRunQueue.executeActions(info.mHandler);
mRunQueue = null;
}
performCollectViewAttributes(mAttachInfo, visibility);
onAttachedToWindow();
...
}
所以当View执行dispatchAttachedToWindow方法时,才将我们最开始发送的Runable对象发送到主线程处理。
但是,当我们在View内部搜索何时调用dispatchAttachedToWindow时,并没有找到。但是View的绘制、测量、布局,都由有父布局开始,所以我们在父布局的中查找调用的地方。
四、ViewGroup的dispatchAttachedToWindow
在ViewGroup的内部我们找到两个地方调用子View的dispatchAttachedToWindow,一是在addViewInner内调用,addViewInner使用addView调用的,这就是我们在手动创建View时,
没有调用父View的addView()方法将View添加进父View时,我们添加的View的post方法不执行的原因。二是在dispatchAttachedToWindow方法内。
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
...
super.dispatchAttachedToWindow(info, visibility);
...
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
final View child = children[i];
child.dispatchAttachedToWindow(info,
combineVisibility(visibility, child.getVisibility()));
}
...
}
ViewGroup的dispatchAttachedToWindow()内主要是调用父类的dispatchAttachedToWindow()方法,然后循环子View,调用子View的dispatchAttachedToWindow()。
而ViewGroup的的dispatchAttachedToWindow()方法什么时候被调用呢?从上面我们知道,子View的dispatchAttachedToWindow()是有父View的dispatchAttachedToWindow()调用。而DecorView是我们所有布局的父布局。所以最终我们的View的dispatchAttachedToWindow()调用是由DecorView的dispatchAttachedToWindow()方法发起的。但是DecorView的dispatchAttachedToWindow()什么时候调用呢。由于我们之前学习了View的绘制流程【http://note.youdao.com/noteshare?id=e58f9423ec333f21ad673f971d340f5b&sub=9E9245E12A244C5395734561C7359B37】,我们知道,View的测量、布局、绘制都是从DecorView开始,都是在ViewRootImpl内的performTraversals()内调用,所以我们接下来看一下performTraversals()核心代码
五、ViewRootImpl的 performTraversals()
performTraversals的核心代码如下
private void performTraversals() {
//是DecorView
final View host = mView;
...
host.dispatchAttachedToWindow(mAttachInfo, 0);
...
performMeasure();
...
performLayout();
...
performDraw();
...
}
从上面我们知道了,原来dispatchAttachedToWindow()的调用是由、ViewRootImpl的 performTraversals() 发起的,但是我们注意到,dispatchAttachedToWindow()的发起是在performMeasure();之前。但是那为什么我们能够在View.post()内获取View的宽高呢。
六、为什么dispatchAttachedToWindow()的发起是在performMeasure(),而我们我们能够在View.post()内获取View的宽高?
由于之前我们分析过ViewRootImpl的performTraversals()执行是在ViewRootImpl的TraversalRunnable内部类中执行。而TraversalRunnable是一个实现Runable的ViewRootImpl的内部类。而scheduleTraversals()的执行是由scheduleTraversals()方法实现。所以分析如下:
- 我们先看一下performTraversals()的执行
ViewRootImpl.java
//1、执行mTraversalRunnable
void scheduleTraversals() {
...
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
...
}
// 2、执行doTraversal();
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
// 3、执行performTraversals();
void doTraversal() {
...
performTraversals();
...
}
从上面我们知道,TraversalRunnable内最终执行了performTraversals()。而TraversalRunnable的执行是由mChoreographer来实现的,那mChoreographer怎么实现执行的呢。
- Choreographer执行TraversalRunnable
Choreographer的核心源码如下:
Choreographer.java
public final class Choreographer {
private final Looper mLooper;
private final FrameHandler mHandler;
//保证没个线程内都是不同的Choreographer实例
private static final ThreadLocal<Choreographer> sThreadInstance =
new ThreadLocal<Choreographer>() {
@Override
protected Choreographer initialValue() {
Looper looper = Looper.myLooper();
if (looper == null) {
throw new IllegalStateException("The current thread must have a looper!");
}
Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);
if (looper == Looper.getMainLooper()) {
mMainInstance = choreographer;
}
return choreographer;
}
};
//主线程的Choreographer
private static volatile Choreographer mMainInstance;
//构造函数
private Choreographer(Looper looper, int vsyncSource) {
mLooper = looper;
mHandler = new FrameHandler(looper);
...
}
//处理Runable
public void postCallback(int callbackType, Runnable action, Object token) {
postCallbackDelayed(callbackType, action, token, 0);
}
@TestApi
public void postCallbackDelayed(int callbackType,
Runnable action, Object token, long delayMillis) {
...
postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
...
synchronized (mLock) {
...
使用Handler将Runable发送出去处理。
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, dueTime);
...
}
}
private final class FrameHandler extends Handler {
public FrameHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DO_FRAME:
doFrame(System.nanoTime(), 0);
break;
case MSG_DO_SCHEDULE_VSYNC:
doScheduleVsync();
break;
case MSG_DO_SCHEDULE_CALLBACK:
doScheduleCallback(msg.arg1);
break;
}
}
}
}
从上面知道,在View的绘制过程也是基于消息机制实现,在View的加载过程中是在主线程实现的,所以Choreographer的Looper是主线程,内部的Handler也是在主线程处理消息。所以最终View的绘制流程是Choreographer内部主线程的Handler发送消息并处理实现。
到这里,我们回头看一下,我们使用View.post()方法时,最终也是有主线程的Handler发送并处理消息实现。由于之前我们学习的Handler机制【http://note.youdao.com/noteshare?id=8670d5fcdde6bf53683fa58f489ca1d3&sub=ECF647152E3B4D42BF4DA272B156E6FB】
在Looper内循环取出消息的方式来处理消息,当一个消息处理完之后再取出另一个消息,由Handler处理。既然这样,我们的View的绘制加载和View.post()都是又主线Handler来,所以只有当View的绘制加载的消息完成之后,才会处理我们View.pos()发送来的消息,所以我们才够在View.post()内获取到View的宽高。
网友评论