View.post Handler.post 以及 IdleHandler 区别分析

先分析一下View.post 和 Handler.post 的区别

在分析之前 先写一个例子

public class MyView extends LinearLayout {

    String TAG = "MyView";
    Handler handler = new Handler();

    public MyView(Context context) {
        super(context);
        init();
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        this.setWillNotDraw(false);
        Log.e(TAG, "oncreat");
        handler.post(new Runnable() {
            @Override
            public void run() {
                Log.e(TAG, "handler.post run ");
            }
        });
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                Log.e(TAG, "view.post run ");
            }
        };
        this.post(runnable);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        Log.e(TAG, "onMeasure  ");
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        Log.e(TAG, "onLayout  ");
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Log.e(TAG, "onDraw  ");
    }


    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        Log.e(TAG, "onAttachedToWindow  ");
    }
}

分别打印View 的若干个声明周期,并使用handler.post 和 view.post 打印两个log

先不说答案,大家可以先想一下

OK 我们来看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的Handler post方法

那么mAttachInfo 是在何处赋值的呢

    void dispatchAttachedToWindow(AttachInfo info, int visibility) {
        mAttachInfo = info;
        。。。。。。
    }

是在dispatchAttachedToWindow的时候

而View的dispatchAttachedToWindow其实是在performTraversals里面调用的 

那也就是说 如果我们在View初始化的时候 调用view.post方法,这个时候View还没有开始layout ,这个时候mAttachInfo一定是null

于是post方法就走了getRunQueue().post(action);这里

继续看源码

    static HandlerActionQueue getRunQueue() {
        HandlerActionQueue rq = sRunQueues.get();
        if (rq != null) {
            return rq;
        }
        rq = new HandlerActionQueue();
        sRunQueues.set(rq);
        return rq;
    }
public class HandlerActionQueue {
    private HandlerAction[] mActions;
    private int mCount;

    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++;
        }
    }

    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;
        }
    }

    private static class HandlerAction {
        final Runnable action;
        final long delay;

        public HandlerAction(Runnable action, long delay) {
            this.action = action;
            this.delay = delay;
        }

        public boolean matches(Runnable otherAction) {
            return otherAction == null && action == null
                    || action != null && action.equals(otherAction);
        }
    }
}

可以看到 所有的任务被预加载到了 HandlerAction[] mActions; 中保存了起来 外部调用executeActions来执行

再去找执行调用的地方

    void dispatchAttachedToWindow(AttachInfo info, int visibility) {
        
        。。。。。。。。。。。。。。

        if (mRunQueue != null) {
            mRunQueue.executeActions(info.mHandler);
            mRunQueue = null;
        }
        。。。。。。。。。。。。。
    }

又找到了dispatchAttachedToWindow的地方

现在我们知道了 当dispatchAttachedToWindow被调用的时候 view.post方法中被加入的任务才真正开始执行

而dispatchAttachedToWindow虽然在performMeasure和performLayout之前调用,但是被加入到消息队列中的消息一定会比主线

程中的measure和layout的同步代码调用要晚,所以篇头的那个问题 我们有了答案

log 输出顺序为

符合预期结果

所以我们得出结论

View.post方法与Handler.post方法区别在于

当View还没有初始化完成时,该Runnable会被缓存起来,待View完成layout之后再执行

我们可以用这个机制来准确判断View开始绘制第一帧的时机

 

最后再来说一下IdleHandler

先看一张从百度上搜过来的图,因为找不到原作者了所以先不贴出处

这是一张讲解Handler的图,从图中可以很容易理解Handler 消息队列的相关逻辑

我们现在把这个图稍微改一下

解释一下

我们把IdleHandler 理解成一个IdleMessage

所有的IdleMessage都被存储在MessageQueue里面

当MessageQueue里面所有的message都被执行完,队列已经清空的时候(这被认为是一种idle状态)

IdleMessage就会被取出执行(注意IdleMeaage不是插入在message队列中间,而是独立存在的)

所以IdleHandler的执行时间是不确定的,如果MessageQueue里面一直有message

那么IdleHandler可能一直都执行不了(当然这种情况不太可能)

所以使用IdleHandler来执行一些启动时的耗时操作是非常NICE的,可以有效的提升启动速度

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值