View.post(Runnable)
为什么可以得到 View 的真实宽高Handler.post(Runnable)
和View.post(Runnable)
有什么区别- 在
onCreate
、onResume
函数中为什么无法直接得到 View 的真实宽高View.post(Runnable)
中的 Runnable 是由谁来执行的,可以保证一定会被执行吗
View.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;
}
AttachInfo 是 View 的静态内部类,每个View都会持有一个AttachInfo ,它默认为null;
AttachInfo赋值
View中能给mAttachInfo赋值的地方只有一处,在 dispatchAttachedToWindow()方法里赋值
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mAttachInfo = info;
//省略。。。
}
初始化AttachInfo
在ViewRootImpl#performTraversals中执行dispatchAttachedToWindow且只执行一次
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView;
//省略。。。
if (mFirst) {
//省略。。。
host.dispatchAttachedToWindow(mAttachInfo, 0);
mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
dispatchApplyInsets(host);
//Log.i(mTag, "Screen on initialized: " + attachInfo.mKeepScreenOn);
}
// Execute enqueued actions on every traversal in case a detached view enqueued an action
getRunQueue().executeActions(mAttachInfo.mHandler);
...
performMeasure();
...
performLayout();
...
performDraw();
}
执行顺序
虽然在程序执行顺序上getRunQueue().executeActions(mAttachInfo.mHandler)先添加任务到消息队列,然后执行View测量performMeasure();然而Android
系统是由事件驱动的,故需要将当前任务执行完成之后,再次轮询消息队列中的其他任务。所以可以测量出View的宽高;
可以理解把任务提交给Looper轮询中-但自己本身正在执行-需要执行完成再次启动下一次轮询
当前AttachInfo 为空是需查看#HandlerActionQueue #本地存储队列/执行任务
private HandlerActionQueue getRunQueue() {
if (mRunQueue == null) {
mRunQueue = new HandlerActionQueue();
}
return mRunQueue;
}
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++;
}
}
ViewRootImpl.getRunQueue().post(action);#7.0之前
getRunQueue().post(action);//7.0之后
#每个线程存在一个#7.0之前
static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>();
#开启执行队列#故子线程和主线程执行post获取的不是同一个队列RunQueue getRunQueue().executeActions(mAttachInfo.mHandler);
不是不可以在子线程中调用View.post,要在View.onAttachToWindow之后在执行;
每个View维护一个队列#7.0之后
private HandlerActionQueue mRunQueue; getRunQueue().executeActions(mAttachInfo.mHandler);
在老API中任务队列存储在#每一个线程存在#ThreadLocal
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;
}
static RunQueue getRunQueue() {
RunQueue rq = sRunQueues.get();
if (rq != null) {
return rq;
}
rq = new RunQueue();
sRunQueues.set(rq);
return rq;
}
所以在子线程中使用View.post()且在AttachInfo未注册时# getRunQueue时在主线程中调用主线程的ThreadLocal#获取不到子线程存储的ThreadLocal#则存在子线程任务未执行的风险