天天看點

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