View.post(Runnble)的一点小问题详解手机开发
今天刚好看到一个问题,为什么onCreate() 中使用 View.post(Runnable)可以拿取到View的宽高,第一想法就是内部利用handler将Runnbale加入主线程MessageQueue,执行完测量任务之后再执行Runnable。
点开源码之后发现好像没那么简单,不过也没那么难
public boolean post(Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { return attachInfo.mHandler.post(action); getRunQueue().post(action); return true;
在post()方法中,如果有attachInfo,直接attachInfo.mHandler.post(),若没有就getRunQueue().post();
看下第一种情况,attachInfo是什么时候不为null,还有mHandler是哪里的Handler?// View void dispatchAttachedToWindow(AttachInfo info, int visibility) { mAttachInfo = info; .... if (mRunQueue != null) { mRunQueue.executeActions(info.mHandler); mRunQueue = null;
mAttachInfo 是在diapatchAttachedToWindow()赋值的,ViewRootImpl的performTraversals()会调用ViewGroup 的 diapatchAttachedToWindow(),最后会调用子View的diapatchAttachedToWindow()
// ViewRootImpl private void performTraversals() { // 如果是首次绘制 if (mFirst) { host.dispatchAttachedToWindow(mAttachInfo, 0); // ViewRootImpl final ViewRootHandler mHandler = new ViewRootHandler(); // ViewRootImpl public ViewRootImpl(Context context, Display display) { mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this, context);
从上面代码可以看出,mAttachInfo在ViewRootImpl构造,并且performTraversals(),若i是首次绘制,会把mAttachInfo赋给View。
View里面的Handler其实就是ViewRootImpl的ViewRootHandler 。
ViewRootHandler直接在主线程构造,所以是消息处理都是在主线程中。
小结:diapatchAttachedToWindow()后,mAttachInfo不为null,就可以直接通过ViewRootHandler处理Runnable,最后能够获取View宽高。
但是不是 diapatchAttachedToWindow()之前,就已经测量好宽高了呢?
再看下第二种情况,getRunQueue().post(action);// View.java private HandlerActionQueue mRunQueue; private HandlerActionQueue getRunQueue() { if (mRunQueue == null) { mRunQueue = new HandlerActionQueue(); return mRunQueue; // HandlerActionQueue public class HandlerActionQueue { private HandlerAction[] mActions; private int mCount; 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;
这里就直接说结果了,在attachInfo为null的时候,View.post()会将Runnable保存在HandlerActionQueue中,等待dispatchAttachedToWindow() 的时候,mRunQueue.executeActions(info.mHandler) 会执行保存在HandlerActionQueue的Runnbale。
可见,我没有考虑到测量绘制还没开始的情况,如果测量还没开始就需要一个数组将Runnable存起来,在测量结束后执行。
但是都同样存在一个问题:是不是 diapatchAttachedToWindow()之前,就已经测量好宽高了呢?
// ViewRootImpl private void performTraversals() { host.dispatchAttachedToWindow(mAttachInfo, 0); performMeasure(); performLayout(); performDraw();
上面的伪代码指出,performTraversals()中是先执行dispatchAttachedToWindow(),后面才执行measure()、layout()等测量,这个怎么保证post.view()肯定能拿到宽高?
后面终于搞清楚了!
原来无论是perfromTraversals,还是后面的Runnable,都是保存在MessageQueue,Looper会执行完当前的任务,再从MessageQueue取出下个任务接着执行;
第一种情况,attachInfo.mHandler.post(action),和第二种情况,在dispatchAttachedToWindow executeActions(),都是增加任务到在主线程的MessageQueue中,而Looper只会处理完当前的测量任务才会去执行View.post的任务。
可以看下面的示意图,当前还在执行performTraversals(),后续的任务还在MessageQueue等待执行。
6294.html
app程序应用开发手机开发无线开发移动端开发相关文章
- AAAI2021 | 在手机上实现19FPS实时的YOLObile目标检测,准确率超高
- 用Service+Broadcast解决倒计时过程中Activity被销毁的问题详解手机开发
- Android加载Gif图片的一般方法:Movie实现详解手机开发
- 利用CIDetector来人脸识别详解手机开发
- iOS获取当前设备信息详解手机开发
- iOS 浮点数四舍五入的方法详解手机开发
- Android开发中遇到的问题(三)——eclipse创建android项目无法正常预览布局文件详解手机开发
- Android开发中遇到的问题(一)——Android模拟器端口被占用问题的解决办法详解手机开发
- [android] 天气app布局练习(四)详解手机开发
- [android] 请求码和结果码的作用详解手机开发
- [android] 采用断点调试的方式观察pull解析的流程详解手机开发
- mac 搭建Android开发环境详解手机开发
- onDraw(canvas)和dispatchDraw(canvas)方法详解手机开发
- android 的android httpClient详解手机开发
- android获取设备唯一标示详解手机开发
- 使用LRU算法缓存图片,android 3.0详解手机开发
- Android优化之Hardware Layer详解手机开发
- Activity之间传递大数据问题详解手机开发
- android调用服务端的js详解手机开发
- 微信小程序之跨界面传参详解手机开发
- StatusBarUtil备份详解手机开发
- SurfaceFlinger学习之路(一)View的绘制流程详解手机开发
- DreamService 和 DreamManagerService 问题分析详解手机开发
- Android setResult 的一点小问题详解手机开发
- 华为 P50 系列引入业界首创的手机摄影技术 XD Optics 计算光学
- 打破人机互动的第四面墙——你很快就会知道你的手机最爱的颜色
- 手机掌握阿里云Redis,降低过期风险(手机阿里云Redis过期)