天天看点

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

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 = ; 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 ,所以效果同第二条。