view.post 和handler发送消息的区别

有个同事问我这个问题,我们往主线程发送一个runnable,有两种方法可以使用,一种是定义一个主线程的handler,通过handler.postRunnable()来执行,一种方法是通过view.post来执行,那么这两种方式有什么区别,具体使用时应该怎么选择。
我们首先要明白的就是两种方式有什么区别,要搞清楚这个问题,就要分析系统究竟是怎么实现的。对于handler.postRunnable 这个不用说,就是往主线程的消息队列扔了一个消息,后面looper会从消息队列取出这条消息,执行这个runnable。
对于view.post 方法就要剖析源码了:

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

首先是判断attachInfo 是否为空,如果不为空的话,就从attachInfo.mHandler来post这个runnable对象,看这个handler的注释:


        /**
         * A Handler supplied by a view's {@link android.view.ViewRootImpl}. This
         * handler can be used to pump events in the UI events queue.
         */
        final Handler mHandler;

注释明白说了,可以用过handler来往ui线程发送消息;
那这个attachInfo 是什么时候赋值,什么情况下为空,再看源码,

    /**
     * {@hide}
     */
    AttachInfo mAttachInfo;

...


    /**
     * @param info the {@link android.view.View.AttachInfo} to associated with
     *        this view
     */
    void dispatchAttachedToWindow(AttachInfo info, int visibility) {
        //System.out.println("Attached! " + this);
        mAttachInfo = info;
        ...
        }

mAttachIn默认就是空,dispatchAttachedToWindow这里对mAttachIn赋值,从注释看这个函数的执行时机是这个view被关联到window的时候;

    void dispatchDetachedFromWindow() {
        ....
        mAttachInfo = null;
        ....
    }

所以就是当view还没有关联到一个具体的window的时候,这个attachinfo就是空;
根据源码,如果attachinfo未空的话将执行下面的代码:

        // Assume that post will succeed later
        ViewRootImpl.getRunQueue().post(action);

ViewRootImpl.getRunQueue()的源码:

    static RunQueue getRunQueue() {
        RunQueue rq = sRunQueues.get();
        if (rq != null) {
            return rq;
        }
        rq = new RunQueue();
        sRunQueues.set(rq);
        return rq;
    }

再看RunQueue的代码:

    /**
     * The run queue is used to enqueue pending work from Views when no Handler is
     * attached.  The work is executed during the next call to performTraversals on
     * the thread.
     * @hide
     */
    static final class RunQueue {
        private final ArrayList<HandlerAction> mActions = new ArrayList<HandlerAction>();

        void post(Runnable action) {
            postDelayed(action, 0);
        }

        void postDelayed(Runnable action, long delayMillis) {
            HandlerAction handlerAction = new HandlerAction();
            handlerAction.action = action;
            handlerAction.delay = delayMillis;

            synchronized (mActions) {
                mActions.add(handlerAction);
            }
        }


        void executeActions(Handler handler) {
            synchronized (mActions) {
                final ArrayList<HandlerAction> actions = mActions;
                final int count = actions.size();

                for (int i = 0; i < count; i++) {
                    final HandlerAction handlerAction = actions.get(i);
                    handler.postDelayed(handlerAction.action, handlerAction.delay);
                }

                actions.clear();
            }
        }
        }

看到这里应该明白其实就是往RunQueue的mActions这个ArrayList里添加数据,执行的函数是executeActions(Handler handler),而这个方法的调用地方是在:

    private void performTraversals() {
            ....

        // Execute enqueued actions on every traversal in case a detached view enqueued an action
        getRunQueue().executeActions(mAttachInfo.mHandler);
        ....
        }

这就跟清楚了,就是在对view进行绘制的时候会把mActions
里的内容全部通过主线程handler发送到主线程消息队列。
这样就是把view.post分析清楚了;
总结下: view.post也是往主线程发消息,只是发消息要看view的状态,如果view已经被关联到window的话,直接通过handler发送,如果不是的话,则把消息添加到RunQueue.mActions中,到下次view绘制时再把mAction中的消息发到主线程消息队列,这里如果aAction中的runnable对象没有被及时发出去的话,回导致内存泄露,以为runnable常常作为匿名内部类,会持有对外部类的引用.
从使用上看在有view引用变量的情况下,使用view.post方便,不需要自己去定义一个handler,定义handler一不小心就会引起内存泄露。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值