博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
View.post()的工作原理
阅读量:4180 次
发布时间:2019-05-26

本文共 6547 字,大约阅读时间需要 21 分钟。

前言:

        如果你对Handler的工作原理比较了解的话,那么对于post() 就比较好了解了,因为post() 的内部用的就是Handler,但是它里面是如何实现的,这就是本节的重点。如果你对这个方法有个清晰的了解的话,那么在开发的时候,有时候就不必自己去维护一个handler了,当然这里使用不当也是会有内存泄露的,就和自己维护的Handler一样,这也是我们在使用的时候需要注意的。对于Handler处理消息回调的接口总共有三个并且是有优先级的,post()使用的就是Message中callback,如果你对Handler还不了解,可以看一看。

源码分析:

        先来看一下View.post()方法:

public boolean post(Runnable action) {    final AttachInfo attachInfo = mAttachInfo;    if (attachInfo != null) {        //使用attachInfo中的Handler的post()方法,handler用过的人应该看下就明白了,        //不明白的应该就是attachInfo这个到底是什么,后面会重点分析        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.    //这里做的就是当attchInfo为null时,将action暂时保存在一个数组中,当attachInfo不为null时,    // 再交由Handler处理    getRunQueue().post(action);    return true;}

看到这里,你可能会有两个点不太明白:

        一个是attachInfo是如何赋值的;

        二是getRunqueue.post()是如何暂时保存任务,注意,这里用的是暂时。

那么我们先来看看getRunqueue()给我们返回的什么:

private HandlerActionQueue getRunQueue() {    if (mRunQueue == null) {        mRunQueue = new HandlerActionQueue();    }    return mRunQueue;}

那我们再去看看HandlerActionQueue这个类是如何实现:

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];            }            //GrowingArrayUtils是一个工具类,这里的作用只是对数组进行判断,            // 如果数组到达容量后,会对数值进行扩容后再将任务放进去            mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);            mCount++;        }    }    public void removeCallbacks(Runnable action) {        synchronized (this) {            final int count = mCount;            int j = 0;            final HandlerAction[] actions = mActions;            for (int i = 0; i < count; i++) {                if (actions[i].matches(action)) {                    // Remove this action by overwriting it within                    // this loop or nulling it out later.                    continue;                }                if (j != i) {                    // At least one previous entry was removed, so                    // this one needs to move to the "new" list.                    actions[j] = actions[i];                }                j++;            }            // The "new" list only has j entries.            mCount = j;            // Null out any remaining entries.            for (; j < count; j++) {                actions[j] = null;            }        }    }    //这里就是将我们保存的任务循环遍历交给传进来的Handler进行处理,现在我们可以大胆的猜测一下,    // attachInfo不为null的时候,应该就是将Handler从这里传进来的,这里先揭个密,还真是。下面马上分析到    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;        }    }    public int size() {        return mCount;    }    public Runnable getRunnable(int index) {        if (index >= mCount) {            throw new IndexOutOfBoundsException();        }        return mActions[index].action;    }    public long getDelay(int index) {        if (index >= mCount) {            throw new IndexOutOfBoundsException();        }        return mActions[index].delay;    }    //对传进来的任务和时间进行封装的一个类    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);        }    }}

这个类还是比较简单的,很容易就看懂,它主要是将传进来的任务进行封装后在保存在数组中。当需要将这些任务交由handler处理的时候,我们看到,这里提供了一个方法executeActions(Handler h),就是将任务循环遍历交由Handler处理,HandlerActionQueue看完了,接下来就该去找找attachInfo了。

        在View中我们搜素一下给mAttachInfo赋值的地方,很快就发现了是在dispatchAttachToWindow()中对mAttachInfo进行赋值的:

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

这是dispatchAttachToWindow()方法中的一部分,可以看到暂时保存起来的任务通过executeActions()将任务交给了Handler处理,到这,我们唯一还要知道就是dispatchAttachToWindow()这个是在什么时候调用。但当我们再次进行搜索的时候发现找不到了,这下该怎么继续呢?我们知道,view的分发肯定是从ViewGroup中来的,那我们就该去ViewGroup中找一找,还真有:

void dispatchAttachedToWindow(AttachInfo info, int visibility) {    mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;    super.dispatchAttachedToWindow(info, visibility);    mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;    final int count = mChildrenCount;    final View[] children = mChildren;    for (int i = 0; i < count; i++) {        final View child = children[i];        child.dispatchAttachedToWindow(info,                combineVisibility(visibility, child.getVisibility()));    }    final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();    for (int i = 0; i < transientCount; ++i) {        View view = mTransientViews.get(i);        view.dispatchAttachedToWindow(info,                combineVisibility(visibility, view.getVisibility()));    }}
这里就是如果ViewGroup有子view,循环调用子view的dispatchAttachToWindow()方法,还是挺简单的。

        到ViewGroup后,我们就应该想到这个应该是从View树的最顶层传下来的,而对于View树最终会在resume的时候传递到ViewRootImpl中去,所有,现在就该去ViewRootImpl中找找了,我们可以在performTranversals()中看到:

host.dispatchAttachedToWindow(mAttachInfo, 0);

终于找到了,原来就是从这里对view的mAttachInfo进行赋值的,并且一个view树共用的是同一个AttachInfo,在ViewRootImpl的构造方法中:

mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,context);

到此,整个流程就全清楚了。

总结:

        1、首先得明确performTranversals()这个方法是在Activity的resume这个时期调用的,也就是mAttachInfo也是在这个时候进行赋值的;

        2、有时候我们可能会在Activity的onCreate()利用view.post()去获取view的宽高,这个是怎么做到的呢?从上面的整个流程下来,我们知道,一开始mAttachInfo是null,那么,post()就会将传进来的的任务暂时保存起来,等到resume的时候通过dispatchAttachToWindow()对mAttachInfo进行赋值,并将缓存起来的任务全部交由mAttachInfo中的Handler处理,这里有一点需要清楚,Android是基于消息驱动来处的,Activity的生命周期也是作为Handler的消息进行分发的,所以这些交给Handler处理的任务要等到目前的消息回调事件处理完才会触发,也就是说,这些任务执行的时间实在view的的测量、布局和绘制的后面,所以在post()中可以拿到view的宽高,并且也一定是在UI线程中执行的。

        3、这里在提一次,使用的时候注意可能存在的内存泄露。

        有哪里看不懂的欢迎留言!!!

        

转载地址:http://vzhai.baihongyu.com/

你可能感兴趣的文章
缓冲区溢出学习
查看>>
Excel高级使用技巧
查看>>
速算,以后留着教孩子
查看>>
让你变成ps高手
查看>>
在可执行jar中动态载入第三方jar(转贴)
查看>>
考虑体积重量的01背包问题—基于遗传算法
查看>>
K-means 聚类算法
查看>>
带约束的K-means聚类算法
查看>>
约束优化方法
查看>>
VRPTW建模与求解—基于粒子群算法
查看>>
数据结构与算法(1):大O表示法
查看>>
Java学习知识树
查看>>
文科生,你为啥学编程?
查看>>
使用Eclipse时出现Unhandled event loop exception错误的有效解决办法
查看>>
JAVA之路:第一章 JAVA入门初体验
查看>>
菜鸟文科生的java之路:运算符
查看>>
菜鸟文科生的java之路:变量和常量
查看>>
菜鸟文科生的java之路:流程控制语句
查看>>
北海糖:Java初阶练习题
查看>>
不知道什么是数组?看这里就好了
查看>>