最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

DEVLOG 10.21 Android UI體系知識&面試題

2021-10-21 15:46 作者:房頂上的鋁皮水塔  | 我要投稿

參考內(nèi)容:AndroidUI體系

問題:

  1. 在Activity中Window和View如何分工協(xié)作?

  2. 何時在Activity中獲取View寬高?

這篇文章主要是回答以上的問題,但是在回答以上的問題中也會出現(xiàn)一些子問題。弄清楚這些問題,有利于我們了解Window WindowManager Activity View的關(guān)系。

在Activity中Window和View如何分工協(xié)作?

我們知道ActivityThread相當(dāng)于是App的主函數(shù)類,Activity#onCreate的開始可以追溯到ActivityThread#handleLaunchActivity中:

handleLaunchActivity中首先初始化了WindowManagerGlobal,從名字可以看出,這個類一定和Window以及WindowManager是有關(guān)的,至于這個類如何和Window和WM產(chǎn)生關(guān)聯(lián),我們待會會總結(jié)。然后在handleLaunchActivity中,又調(diào)用了performLaunchActivity,在這個方法中會調(diào)用Activity#attach。

ActivityThread#performLaunchActivity

performActivity通過反射機制創(chuàng)建了Activity的實例,并且構(gòu)建了Application的實例,當(dāng)attach方法執(zhí)行完成之后,使用記錄當(dāng)前的Activity狀態(tài)是ON_CREATE。那么,不言而喻,attach方法中應(yīng)該會調(diào)用我們實現(xiàn)的Activity#onCreate回調(diào)。接著我們來看看Activity#attach方法:

Activity#attach

因為Activity的回調(diào)方法中通常我們使用setContentView加載當(dāng)前resId指定的布局,但是這個布局是在DecorView的ContentView中,而DecorView又在PhoneWindow中。所以當(dāng)前Activity#attach時需要創(chuàng)建PhoneWindow的實例,并且綁定PhoneWindow到WindowManager中。

繼續(xù)看ActivityThread#performLaunchActivity

當(dāng)Activity#attach執(zhí)行完成之后,performLaunchActivity繼續(xù)執(zhí)行,會執(zhí)行到這一行代碼:

在Instrumentation#callActivityOnCreate中會調(diào)用Activity#performCreate,這里面就回調(diào)用我們寫的onCreate回調(diào)。因此,一個不太標(biāo)準(zhǔn)的從ActivityThread#handlePerformLaunchActivity到Activity#onCreate的時序圖,如下圖所示:

雖然我們知道在Activity的生命周期方法調(diào)度完成時我們可以看到我們寫的布局文件,但是我們目前并不能看到Window和View的關(guān)系,于是我們可以猜想,既然onCreate方法中沒有,那么,Window加載View的代碼可能會在onResume上。這是因為在官方文檔中也說明,onResume是程序到前臺的標(biāo)志。

ActivityThread#handleResumeActivity

ActivityThread#handleResumeActivity方法首先調(diào)用performResumeActivity,在performResumeActivity完成之后在會調(diào)用WindowManager#addView向Window中添加布局。

所以,實際上布局是在onResume完成之后才被加載在PhoneWindow中的,不過具體的內(nèi)容我們還是需要看看performResumeActivity:

ActivityThread#performResumeActivity

套路和前面的都差不多,在ActivityThread中執(zhí)行Activity#performResume然后轉(zhuǎn)到啟動相關(guān)類Instrumentation#callActivityOnResume,在執(zhí)行onResume回調(diào)。


因此我們可以做一個小小的總結(jié),關(guān)于Activity中Window和View,他們之間的合作關(guān)系和Activity的生命周期是密切相關(guān)的:

onCreate階段:

但是在onCreate中,并不會把View加載到PhoneWindow中,這個說來也非常好理解,畢竟我們在回調(diào)中才解析布局文件xml,怎么會在PhoneWindow創(chuàng)建之前addView呢?

onResume階段:

ViewRootImpl如何成為Window和View的橋梁?

剛才我們看到WindowManager可以將DecorView加載到PhoneWindow中,這個過程還可以仔細(xì)地分析一番。在分析之前我們先總結(jié)一下Window相關(guān)的類之間的關(guān)系:

ViewManager只是一個接口,定義了基本的對于View的添加和刪除工作

WindowManager也是一個接口,但是我們通常操作的都是這個類,他的實現(xiàn)類是WindowManageImpl。WindowManagerImpl又通過將職責(zé)委托給WindowManagerGlobal實現(xiàn),之所以使用這么復(fù)雜的【套娃】邏輯,好處有兩點:

  1. 這是一種外觀模式,我們通過WindowManager就可以操作WindowManagerGlobal和framework層通信。

  2. WindowManagerGlobal也是一個全局單例。根據(jù)上面的代碼分析,創(chuàng)建Activity就回創(chuàng)建對應(yīng)的WindowManager和PhoneWindow,但是這些所有的WindowManager都基于WindowManagerGlobal,節(jié)省內(nèi)存。

回到問題【ViewRootImpl如何成為Window和View的橋梁?】本身,我們需要查看一下WindowManager#addView的代碼:

在WindowManagerGlobal中初始化了ViewRootImpl,然后調(diào)用了setView。跟蹤ViewRootImpl#setView可以發(fā)現(xiàn)這個方法最后會調(diào)用WindowSession#addToDispaly,再調(diào)用WindowManagerService#addWindow,整體的調(diào)用鏈如圖:

所以可以看到ViewRootImpl確實充當(dāng)了一個橋梁,上面抓住了WindowManager,下面連接的是WindowSession(是IWindowSession.Stub的實現(xiàn)類,是Binder機制的一部分)。


如何onResume中獲取View的寬高?

這個問題可以轉(zhuǎn)換成另外的一個子問題,View#measure是在什么時候執(zhí)行的?

View#measure的執(zhí)行時機

上面已經(jīng)說過個, onResume會先于WindowManager#addView,所以回調(diào)函數(shù)本身會在測量之前執(zhí)行,這樣在onResume中嘗試獲取寬高一定會返回0,更不用說在onCreate中。

解決問題的思路:

  1. handler.post( Runnable {}, delay):這里不可以不加delay。如果不加delay,這個消息在消息隊列中還是會先執(zhí)行,而我們的目的是想等到onResume執(zhí)行完成,WindowManager#addView之后再拿到View寬高,這個大小設(shè)置為100ms。

  2. View.post(Runnable {}): View.post的原理就是將當(dāng)前的Runnable放入了HandlerActionQueue的數(shù)組中,然后在ViewRootImpl#performTraversals中執(zhí)行。ViewRootImpl#performTraversals是執(zhí)行測量 布局 繪制的開始,肯定也會在WindowManager#addView之后

3. ?onWindowFocusChanged回調(diào):當(dāng)失去焦點時會被調(diào)用

4. addOnGlobalLayoutListener:當(dāng)ViewTree變化的時候會被調(diào)用。


在子線程中能不能更新UI?

這個例子中代碼會報錯嗎?

其實并不會,因為在這里,textView被setContentView加載之后設(shè)置setText時只是將String變量存儲到TextView中,此時TextView并沒有開始performTraversals,不會檢查UI線程,所以沒有問題。


DEVLOG 10.21 Android UI體系知識&面試題的評論 (共 條)

分享到微博請遵守國家法律
社旗县| 聊城市| 阳山县| 昌吉市| 锡林郭勒盟| 新和县| 湘潭市| 花莲县| 泰来县| 永丰县| 忻城县| 巍山| 基隆市| 鹤壁市| 瓦房店市| 东台市| 蛟河市| 天峨县| 乐山市| 秦皇岛市| 明光市| 巴里| 清水河县| 宁晋县| 渝中区| 当雄县| 霍邱县| 桦甸市| 平顺县| 遵义市| 武夷山市| 马公市| 湖南省| 新泰市| 南皮县| 托克托县| 灵山县| 阳江市| 香港 | 太仓市| 金秀|