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