AndroidView.post(Runable)某些情况不执行的原因

    xiaoxiao2025-02-10  14

    Android View.post(Runable)某些情况不执行的原因

    转载请注明http://blog.csdn.net/chen_senhua/article/details/52202413 前几天在改项目UI的时候,发现一个bug,第一次进入APP的时候有个Bitmap不显示, 点击换图后又能显示,打个断点,才发现问题出在View.post(Runable),竟然没有执行。

    之前也没看里面的实现,只是猜测里面有个Handler,现在有空就来看看里面的实现 打开View类找到方法

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

    首先看mAttachInfo 何时赋值的

    从代码中可以猜出,十有八九是mAttachInfo ==null 导致不执行的,既然是初始化的时候不显示,可以推断是赋值问题,找到mAttachInfo 的赋值方法

    void dispatchAttachedToWindow(AttachInfo info, int visibility) { mAttachInfo = info; //... onAttachedToWindow(); //... }

    可以看到赋值是在 dispatchAttachedToWindow 的时候进行的,而dispatchAttachedToWindow 是在 ViewRootImpl 类的performTraversals 调用的,这方法在视图的初始化会调用,在视图发生某些改变时也会调用。(ViewRootImpl 是View 和WindowManager 的交互类). 因此可以得出结论在onAttachedToWindow之后post,必定是有效果的。

    然后再看看mAttachInfo 没有被赋值的情况下怎么处理的

    注释说“Assume that post will succeed later“我只是assume 他会执行,意味着-那就有一定几率不执行咯, 这个情况下他把runable放到一个队列中了,到底是放哪了呢,再看代码

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

    其中 sRunQueues 是ThreadLocal的实例,(ThreadLocal是一个和线程绑定的数据存储类,在不同的线程set的值得在相应相应线程才能取到,大体实现是把自身作为键值放到了Thread的Map中),他把runable放到一个ThreadLocal中去了,那么他何时去取呢?再次打开ViewRootImpl 类

    performTraversals() { //.. getRunQueue().executeActions(mAttachInfo.mHandler,0) //.. } 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(); } }

    上面可以看出runable绕了一圈又回到了mAttachInfo.mHandler 的怀抱。

    所以我们可以得出如下结论

    1)在View的onAttachedToWindow 之后进行post,不论在什么线程都会调用,postDelay同理。 2)在View的onAttachedToWindow 之前进行post,在主线程,可能要隔一段时间才会调用(等待 下一次 performTraversals ),在支线没有特殊处理不会被调用,postDelay同理,并且delay的时间偏差可能较大。 3)在一个View没有添加到窗口中时,也就不会调用onAttachedToWindow ,所以效果同第二条。

    转载请注明原文地址: https://ju.6miu.com/read-1296284.html
    最新回复(0)