Android獲取View的寬和高(二)

接上篇: Android獲取View的寬和高(一)

其實(shí)除了ViewTreeObserver這個觀察者類告私,還可以通過View.Post()獲取到View寬高margin值的信息,代碼如下:

 btn02.post(new Runnable() {
            @Override
            public void run() {
                //可以正常獲取到View寬高 margin
                LogUtil.d(" btn02.post" + btn02.getWidth()+btn02.getTop());
            }
        });

看log日志輸出的時候發(fā)現(xiàn)run方法是在ViewTreeObserver的OnGlobalLayoutListener啦扬,OnPreDrawListener濒翻,OnDrawListener之后執(zhí)行的屁柏,那么它到底什么時候調(diào)用的呢啦膜,點(diǎn)開view的post方法,查看源碼代碼如下:

  /**
     * <p>Causes the Runnable to be added to the message queue.
     * The runnable will be run on the user interface thread.</p>
     *//把該runnable加入到 mq里,最終在ui線程執(zhí)行
     * 
     */
    public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }

        // Postpone the runnable until we know on which thread it needs to run.
        // Assume that the runnable will be successfully placed after attach.
        getRunQueue().post(action);
        return true;
    }
     /**
     * Returns the queue of runnable for this view.
     */
    private HandlerActionQueue getRunQueue() {
        if (mRunQueue == null) {
            mRunQueue = new HandlerActionQueue();
        }
        return mRunQueue;
    }

代碼里包含了一個attachInfo和 HandlerActionQueue淌喻,attachInfo是View在添加到window上的描述信息僧家,HandlerActionQueue姑且看作一個Handler去發(fā)送通知的,繼續(xù)查看AttachInfo的賦值過程裸删,代碼如下:

 /**
         * @param info the {@link android.view.View.AttachInfo} to associated with
         *        this view
         */
        void dispatchAttachedToWindow(AttachInfo info, int visibility) {
            mAttachInfo = info;
            if (mOverlay != null) {
                mOverlay.getOverlayView().dispatchAttachedToWindow(info, visibility);
            }
            registerPendingFrameMetricsObservers();
            performCollectViewAttributes(mAttachInfo, visibility);
            onAttachedToWindow();
            ListenerInfo li = mListenerInfo;
            final CopyOnWriteArrayList<View.OnAttachStateChangeListener> listeners =
                    li != null ? li.mOnAttachStateChangeListeners : null;
            if (listeners != null && listeners.size() > 0) {
                for (View.OnAttachStateChangeListener listener : listeners) {
                    listener.onViewAttachedToWindow(this);
                }
            }
            
            needGlobalAttributesUpdate(false);

            notifyEnterOrExitForAutoFillIfNeeded(true);
        }

mAttachInfo在 dispatchAttachedToWindow中被賦值八拱,它其實(shí)是在ViewRootImpl中構(gòu)造的,(ViewRootImpl 可以理解是一個 Activity 的 ViewTree 的根節(jié)點(diǎn)的實(shí)例涯塔,它是用來管理 DecorView 和 ViewTree)肌稻。在ViewRootImpl構(gòu)造方法為:

 mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,context);

而dispatchAttachedToWindow只會在兩種情況下被調(diào)用:

  1. ViewRootImpl 第一次 performTraversal()時會將整個view tree里所有有view的 dispatchAttachedToWindow() DFS 調(diào)用一遍.
  2. ViewGroup的 addViewInner(View child, int index, LayoutParams params, boolean preventRequestLayout);
    這時我們應(yīng)該是第一種情況下的調(diào)用,我們在去查看ViewRootImpl中的 performTraversal()方法:
private void performTraversals () {
            // cache mView since it is used so much below...
            final View host = mView
            host.dispatchAttachedToWindow(mAttachInfo, 0);
            //viewTreeObserver也在這個方法中調(diào)用    
            mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
            //對每次遍歷操作進(jìn)行排隊(duì)
            getRunQueue().executeActions(mAttachInfo.mHandler);
            // Ask host how big it wants to be//測量過程
            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
            int width = host.getMeasuredWidth();
            int height = host.getMeasuredHeight();
            //onLayout執(zhí)行
            performLayout(lp, mWidth, mHeight);
            //開始繪制
            performDraw();
        }

該方法是View整個繪制過程匕荸;其中 getRunQueue()方法如下:

 static HandlerActionQueue getRunQueue() {
          // sRunQueues是 ThreadLocal<HandlerActionQueue> 對象
        HandlerActionQueue rq = sRunQueues.get();
        if (rq != null) {
            return rq;
        }
        rq = new HandlerActionQueue();
        sRunQueues.set(rq);
        return rq;
    }

忽然找到了親人爹谭,這不就是post里的HandlerActionQueue那個對象么,到目前他們之間到關(guān)系貌似也漸漸清晰起來了榛搔。

總結(jié)一下:

  • 1.View在attachtoWindow之前诺凡,會維護(hù)一個HandlerActionQueue對象,儲存當(dāng)前的runnable 對象践惑,當(dāng)attach to window(ViewRootImpl 執(zhí)行到 performTraversal 方法)的時候交給ViewRootImpl處理腹泌。
  • 2.View的dispatchAttachedToWindow方法也會為當(dāng)前view創(chuàng)建一個attachInfo對象,該對象持有 ViewRootImpl 的引用尔觉,當(dāng) View 有此對象后凉袱,后續(xù)的所有 Runnable 都將直接交給 ViewRootImpl 處理。
  • 3.ViewRootImpl在執(zhí)行performTraversal方法是通過getRunQueue() 方法在 ThreadLocal中維護(hù)一個HandlerActionQueue對象侦铜,ViewRootImpl使用該對象對runnable進(jìn)行短期維護(hù)专甩。
  • 4.但需要注意的是,各個 View 調(diào)用的 post 方法泵额,仍然是由各自的 HandlerActionQueue 對象來入隊(duì)任務(wù)的配深,然后在 View#dispatchAttachedToWindow 的時候轉(zhuǎn)移給 ViewRootImpl 去處理携添。

另外嫁盲,View.post也有不靠譜的地方,API24和23的post方法是有區(qū)別的烈掠,23的代碼如下:

// Android API23 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 來入隊(duì)任務(wù)的
    ViewRootImpl.getRunQueue().post(action);
    return true;
}

再來看一下 HandlerActionQueue#executeActions方法:

 public void executeActions(Handler handler) {
        synchronized (this) {
            final HandlerAction[] actions = mActions;
            for (int i = 0, count = mCount; i < count; i++) {
                final HandlerAction handlerAction = actions[i];
                handler.postDelayed(handlerAction.action, handlerAction.delay);
            }

            mActions = null;
            mCount = 0;
        }
    }

post方法不靠譜的原因根本上是executeActions() 方法的調(diào)用時機(jī)不同,導(dǎo)致 View 在沒有 mAttachInfo 對象的時候左敌,表現(xiàn)不一樣了瘾蛋。具體可參考這篇日志。

https://juejin.im/post/59ae9366518825243a78f9b6

除此以外矫限,ViewRootImpl 使用 ThreadLocal 來存儲隊(duì)列信息哺哼,在某些情境下佩抹,還會導(dǎo)致內(nèi)存泄漏。詳細(xì)信息可以參考:

https://blog.csdn.net/a740169405/article/details/69668957

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末取董,一起剝皮案震驚了整個濱河市棍苹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌茵汰,老刑警劉巖枢里,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蹂午,居然都是意外死亡栏豺,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進(jìn)店門豆胸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來奥洼,“玉大人,你說我怎么就攤上這事晚胡「茸浚” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵搬泥,是天一觀的道長桑寨。 經(jīng)常有香客問我,道長忿檩,這世上最難降的妖魔是什么尉尾? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮燥透,結(jié)果婚禮上沙咏,老公的妹妹穿的比我還像新娘。我一直安慰自己班套,他們只是感情好肢藐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著吱韭,像睡著了一般吆豹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上理盆,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天痘煤,我揣著相機(jī)與錄音,去河邊找鬼猿规。 笑死衷快,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的姨俩。 我是一名探鬼主播蘸拔,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼师郑,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了调窍?” 一聲冷哼從身側(cè)響起呕乎,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎陨晶,沒想到半個月后猬仁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡先誉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年湿刽,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片褐耳。...
    茶點(diǎn)故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡诈闺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出铃芦,到底是詐尸還是另有隱情雅镊,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布刃滓,位于F島的核電站仁烹,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏咧虎。R本人自食惡果不足惜卓缰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望砰诵。 院中可真熱鬧征唬,春花似錦、人聲如沸茁彭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽理肺。三九已至摄闸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間哲嘲,已是汗流浹背贪薪。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留眠副,地道東北人。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓竣稽,卻偏偏與公主長得像囱怕,于是被迫代替她去往敵國和親霍弹。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評論 2 354