我的应用场景是这样子的
我在一个异步操作中用EventBus 发送了一个事件.
在另外一个Activity 中对这个事件进行接收.
因为是异步所以我使用了View.post方法企图让它回到主线程更新这些数据.(此时我的View 处于Detach 状态)
但是事情并没有这么美好.
我的Runnable方法没有触发Run()方法.
问题出在哪里?
我排查了很久.很久,很久....
我们进入post方法:
/**
* <p>Causes the Runnable to be added to the message queue.
* The runnable will be run on the user interface thread.</p>
*
* @param action The Runnable that will be executed.
*
* @return Returns true if the Runnable was successfully placed in to the
* message queue. Returns false on failure, usually because the
* looper processing the message queue is exiting.
*
* @see #postDelayed
* @see #removeCallbacks
*/
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;
}
因为我的View 处于Detach状态. 所以attachInfo 为空.所以我们进入了
ViewRootImpl.getRunQueue().post(action);
/**
* The run queue is used to enqueue pending work from Views when no Handler is
* attached. The work is executed during the next call to performTraversals on
* the thread.
* @hide
*/
static final class RunQueue {
private final ArrayList<HandlerAction> mActions = new ArrayList<HandlerAction>();
void post(Runnable action) {
postDelayed(action, 0);
}
void postDelayed(Runnable action, long delayMillis) {
HandlerAction handlerAction = new HandlerAction();
handlerAction.action = action;
handlerAction.delay = delayMillis;
synchronized (mActions) {
mActions.add(handlerAction);
}
}
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();
}
}
正常流程是我们用post 方法加入mActions 中,在performTraversals 中调用executeActions()对这些信息进行消耗.
到这里看起来一切都是那么美好.
问题出在哪里?
static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>();
static RunQueue getRunQueue() {
RunQueue rq = sRunQueues.get();
if (rq != null) {
return rq;
}
rq = new RunQueue();
sRunQueues.set(rq);
return rq;
}
sRunQueues 是一个ThreadLocal 类型,也就是说你上面加入的mActions跟主线程中的mActions 不是同一个.
总结:
当你使用View 处于Detach 状态.
调用线程为非主线程.
View.post并不能把你的runnable带到主线程执行.
BTW:
我们可以看到RunQueue 上面有这样的注释.The work is executed during the next call to performTraversals on the thread. 也就是说当你正确使用了post 方法(你的Runnable 正确加入到主线程的mActions ) 那么他也是要在下一个performTraversals
方法中执行.如果 performTraversals 不到来,你的Runnable 是不会执行的.什么时候确保这个快速的到来呢,使用View.invalidate()