问题:
流程:在ImageLoader中,在item create的时候,就利用 view.post 方式请求加载 placeholder Runnable,图片加载完成之后,再在Thread线程中 通过View.post 方式请求加载Image Runnable。
现象:
会出现 概率性Image加载不出来的。
直接原因:
发现是 两次post,第一次先执行的是 Image 的Runnable,然后再执行的placeholder Runnable。
6.0版本View.post代码:
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;
}
分析:
1.按照代码逻辑,首先是 View.post placeholder Runnable,然后再去执行的 View.post Image Runnable,这是合乎逻辑的;
(怀疑过 Handler被堵塞..........甚至怀疑过 View.post 执行的顺序。)
2.查询Android版本(因为在 7.0 8.0 都是正常的),发现 6.0版本的View.post和7.0 、8.0的机制有一点不一样导致:
相同点:在view 没有Attached状态的时,6.0和7.0的View.post 都是放到RunQueue里面去的。
不同点:6.0的post的Runnable任务在心跳到来的时候,执行ViewRootImpl.performTraversals的时候会去通过Handler.post把任务放到MessageQueue中,等待Looper来取出并交给Handler执行。
3.发现问题点就出现 在,
3.1 第一次view.post placeholder Runnable的时候,view 还没进入Attached 状态,所以attachInfo 是null,于是 Runnable放入了 RunQueue中;
3.2 第二次view.post Image Runnable的时候,attachInfo不为null ,直接交给Handler来处理;
3.3 因为RunQueue的任务执行的时机是 心跳到来的时刻,这个时间正好晚于 Image Runnale放进MessageQueue的时刻,导致Placeholder Runnable排在Image Runnable后面;
4.至于在7.0、8.0是OK的原因,是因为这些版本的view.post 的任务,是在view 进入Attached 状态之后才会被执行,这个时候正好 Image Runnable放入Handler的时机比较凑巧的晚于Placeholder Runnable;
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mAttachInfo = info;
...
// Transfer all pending runnables.
if (mRunQueue != null) {
mRunQueue.executeActions(info.mHandler);
mRunQueue = null;
}
.....
}
总结:
尽量使用Handler吧,如果没特别的需求的话;
说实话,以前特么还真么注意这些细节,另外,Android 版本太碎片化导致我们的工作量陡增.....