記一次錯誤的使用View.post(Runnable)

我的應(yīng)用場景是這樣子的
我在一個異步操作中用EventBus 發(fā)送了一個事件.
在另外一個Activity 中對這個事件進行接收.
因為是異步所以我使用了View.post方法企圖讓它回到主線程更新這些數(shù)據(jù).(此時我的View 處于Detach 狀態(tài))
但是事情并沒有這么美好.
我的Runnable方法沒有觸發(fā)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狀態(tài). 所以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 中調(diào)用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 不是同一個.

總結(jié):
當(dāng)你使用View 處于Detach 狀態(tài).
調(diào)用線程為非主線程.
View.post并不能把你的runnable帶到主線程執(zhí)行.

BTW:
我們可以看到RunQueue 上面有這樣的注釋.The work is executed during the next call to performTraversals on the thread. 也就是說當(dāng)你正確使用了post 方法(你的Runnable 正確加入到主線程的mActions ) 那么他也是要在下一個performTraversals方法中執(zhí)行.如果 performTraversals 不到來,你的Runnable 是不會執(zhí)行的.什么時候確保這個快速的到來呢,使用View.invalidate()

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容