• View#post(Runnable)的执行流程


    • View.post(Runnable) 为什么可以得到 View 的真实宽高
    • Handler.post(Runnable)View.post(Runnable)有什么区别
    • onCreateonResume 函数中为什么无法直接得到 View 的真实宽高
    • View.post(Runnable) 中的 Runnable 是由谁来执行的,可以保证一定会被执行吗

    View.post()的执行流程

    1. public boolean post(Runnable action) {
    2. final AttachInfo attachInfo = mAttachInfo;
    3. if (attachInfo != null) {
    4. return attachInfo.mHandler.post(action);
    5. }
    6. // Postpone the runnable until we know on which thread it needs to run.
    7. // Assume that the runnable will be successfully placed after attach.
    8. getRunQueue().post(action);
    9. return true;
    10. }

    AttachInfo 是 View 的静态内部类,每个View都会持有一个AttachInfo ,它默认为null;

    AttachInfo赋值

    View中能给mAttachInfo赋值的地方只有一处,在 dispatchAttachedToWindow()方法里赋值

    1. void dispatchAttachedToWindow(AttachInfo info, int visibility) {
    2. mAttachInfo = info;
    3. //省略。。。
    4. }

     初始化AttachInfo

    ViewRootImpl#performTraversals中执行dispatchAttachedToWindow且只执行一次

    1. private void performTraversals() {
    2. // cache mView since it is used so much below...
    3. final View host = mView;
    4. //省略。。。
    5. if (mFirst) {
    6. //省略。。。
    7. host.dispatchAttachedToWindow(mAttachInfo, 0);
    8. mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
    9. dispatchApplyInsets(host);
    10. //Log.i(mTag, "Screen on initialized: " + attachInfo.mKeepScreenOn);
    11. }
    12. // Execute enqueued actions on every traversal in case a detached view enqueued an action
    13. getRunQueue().executeActions(mAttachInfo.mHandler);
    14. ...
    15. performMeasure();
    16. ...
    17. performLayout();
    18. ...
    19. performDraw();
    20. }

    执行顺序 

    虽然在程序执行顺序上getRunQueue().executeActions(mAttachInfo.mHandler)先添加任务到消息队列,然后执行View测量performMeasure();然而Android 系统是由事件驱动的,故需要将当前任务执行完成之后,再次轮询消息队列中的其他任务。所以可以测量出View的宽高

    可以理解把任务提交给Looper轮询中-但自己本身正在执行-需要执行完成再次启动下一次轮询

    当前AttachInfo 为空是需查看#HandlerActionQueue #本地存储队列/执行任务

    1. private HandlerActionQueue getRunQueue() {
    2. if (mRunQueue == null) {
    3. mRunQueue = new HandlerActionQueue();
    4. }
    5. return mRunQueue;
    6. }
    7. public void postDelayed(Runnable action, long delayMillis) {
    8. final HandlerAction handlerAction = new HandlerAction(action, delayMillis);
    9. synchronized (this) {
    10. if (mActions == null) {
    11. mActions = new HandlerAction[4];
    12. }
    13. mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
    14. mCount++;
    15. }
    16. }

    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

    1. public boolean post(Runnable action) {
    2. final AttachInfo attachInfo = mAttachInfo;
    3. if (attachInfo != null) {
    4. return attachInfo.mHandler.post(action); ①
    5. }
    6. // Assume that post will succeed later
    7. ViewRootImpl.getRunQueue().post(action); ②
    8. return true;
    9. }
    10. static RunQueue getRunQueue() {
    11. RunQueue rq = sRunQueues.get();
    12. if (rq != null) {
    13. return rq;
    14. }
    15. rq = new RunQueue();
    16. sRunQueues.set(rq);
    17. return rq;
    18. }

    所以在子线程中使用View.post()且在AttachInfo未注册时# getRunQueue时在主线程中调用主线程的ThreadLocal#获取不到子线程存储的ThreadLocal#则存在子线程任务未执行的风险

  • 相关阅读:
    语法基础(判断语句)
    推箱子游戏设计与实现(Java+swing+JAWT)
    17.0、Java多线程——synchronized同步方法及同步块
    实时选品系统实现的难点
    springboot+vue前后端分离项目社区物业管理系统设计与实现.rar(项目源码)
    计算机网络 交换机的基本配置
    Python实现BS架构Flask的Web学生管理信息系统
    【餐厅点餐平台|一】项目描述+需求分析
    Mysql数据库文件太大无法导入数据库
    01背包问题
  • 原文地址:https://blog.csdn.net/nuonuonuonuonuo/article/details/133743913