View.post(Runnable)为什么可以得到 View 的真实宽高Handler.post(Runnable)和View.post(Runnable)有什么区别- 在
onCreate、onResume函数中为什么无法直接得到 View 的真实宽高View.post(Runnable)中的 Runnable 是由谁来执行的,可以保证一定会被执行吗
- 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;
View中能给mAttachInfo赋值的地方只有一处,在 dispatchAttachedToWindow()方法里赋值
- void dispatchAttachedToWindow(AttachInfo info, int visibility) {
- mAttachInfo = info;
- //省略。。。
- }
在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
sRunQueues = new ThreadLocal (); #开启执行队列#故子线程和主线程执行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#则存在子线程任务未执行的风险