zoukankan      html  css  js  c++  java
  • Android View post 方法

    解析View.post方法。分析一下这个方法的流程。

    说起post方法,我们很容易联想到Handlerpost方法,都是接收一个Runnable对象。那么这两个方法有啥不同呢?

    Handler的post方法

    先来简单看一下Handlerpost(Runnable)方法。这个方法是将一个Runnable加到消息队列中,并且会在这个handler关联的线程里执行。

    下面是关联的部分源码。可以看到传入的Runnable对象,装入Message后,被添加进了queue队列中。

    Handler 有关的部分源码

        // android.os Handler 有关的部分源码
        public final boolean post(@NonNull Runnable r) {
            return sendMessageDelayed(getPostMessage(r), 0);
        }
    
        private static Message getPostMessage(Runnable r) {
            Message m = Message.obtain();
            m.callback = r;
            return m;
        }
    
        public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
            if (delayMillis < 0) {
                delayMillis = 0;
            }
            return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
        }
    
        public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
            MessageQueue queue = mQueue;
            if (queue == null) {
                RuntimeException e = new RuntimeException(
                        this + " sendMessageAtTime() called with no mQueue");
                Log.w("Looper", e.getMessage(), e);
                return false;
            }
            return enqueueMessage(queue, msg, uptimeMillis);
        }
    
        private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
                long uptimeMillis) {
            msg.target = this;
            msg.workSourceUid = ThreadLocalWorkSource.getUid();
    
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    

    具体流程,可以看handler介绍

    View的post方法

    我们直接跟着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;
    }
    
    private HandlerActionQueue getRunQueue() {
        if (mRunQueue == null) {
            mRunQueue = new HandlerActionQueue();
        }
        return mRunQueue;
    }
    

    可以看到一开始就查询是否有attachInfo,如果有,则用attachInfo.mHandler来执行这个任务。

    如果没有attachInfo,则添加到View自己的mRunQueue中。确定运行的线程后,再执行任务。

    post(Runnable action)的返回boolean值,如果为true,表示任务被添加到消息队列中了。
    如果是false,通常表示消息队列关联的looper正在退出。

    那么我们需要了解AttachInfoHandlerActionQueue

    AttachInfo

    AttachInfoView的静态内部类。View关联到父window后,用这个类来存储一些信息。

    AttachInfo存储的一部分信息如下:

    • WindowId mWindowId window的标志
    • View mRootView 最顶部的view
    • Handler mHandler 这个handler可以用来处理任务

    HandlerActionQueue

    View还没有handler的时候,拿HandlerActionQueue来缓存任务。HandlerAction是它的静态内部类,存储Runnable与延时信息。

    public class HandlerActionQueue {
        private HandlerAction[] mActions;
        
        public void post(Runnable action)
        public void executeActions(Handler handler)
        // ...
    
        private static class HandlerAction {
            final Runnable action;
            final long delay;
            // ...
        }
    }
    

    View的mRunQueue

    将任务(runnable)排成队。当View关联上窗口并且有handler后,再执行这些任务。

    /**
     * Queue of pending runnables. Used to postpone calls to post() until this
     * view is attached and has a handler.
     */
    private HandlerActionQueue mRunQueue;
    

    这个mRunQueue里存储的任务啥时候被执行?我们关注dispatchAttachedToWindow方法。

    void dispatchAttachedToWindow(AttachInfo info, int visibility) {
        // ...
        // Transfer all pending runnables.
        if (mRunQueue != null) {
            mRunQueue.executeActions(info.mHandler);
            mRunQueue = null;
        }
        // ...
    }
    

    这个方法里调用了mRunQueue.executeActions

    executeActions(Handler handler)方法实际上是用传入的handler处理队列中的任务。

    而这个dispatchAttachedToWindow会被ViewGroup中被调用。

    或者是ViewRootImpl中调用

    host.dispatchAttachedToWindow(mAttachInfo, 0);
    

    小结

    View的post方法,实际上是使用了AttachInfohandler

    如果View当前还没有AttachInfo,则把任务添加到了View自己的HandlerActionQueue队列中,然后在dispatchAttachedToWindow中把任务交给传入的AttachInfohandler。也可以这样认为,View.post用的就是handler.post

    我们在获取View的宽高时,会利用View的post方法,就是等View真的关联到window再拿宽高信息。

    流程图归纳如下

    更多请参见Android合集的最近更新

    一个软件工程师的记录
  • 相关阅读:
    linux 中字符映射错误,#、、|、“ 不能输入
    Qt::Key_Return Qt::Key_Enter 区别
    如何监控系统用户实时执行的Linux命令
    a3考卷转2张a4
    kali
    字典序最小问题(贪心)
    nmap 目标指定
    debian 系统安装最新版本nmap方法:
    TransCAD是由美国Caliper公司开发的一套强有力的交通规划和需求预测软件
    GIS 地图的图层(切片/瓦片)概念
  • 原文地址:https://www.cnblogs.com/rustfisher/p/15322722.html
Copyright © 2011-2022 走看看