亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

源碼詳解Android中View.post()用法

 更新時(shí)間:2017年12月18日 11:34:10   作者:請(qǐng)叫我大蘇  
本文通過源碼給大家詳細(xì)分析了在Android中View.post()的用法以及經(jīng)常遇到的問題和解決辦法,一起來學(xué)習(xí)下。

emmm,大伙都知道,子線程是不能進(jìn)行 UI 操作的,或者很多場(chǎng)景下,一些操作需要延遲執(zhí)行,這些都可以通過 Handler 來解決。但說實(shí)話,實(shí)在是太懶了,總感覺寫 Handler 太麻煩了,一不小心又很容易寫出內(nèi)存泄漏的代碼來,所以為了偷懶,我就經(jīng)常用 View.post() or View.postDelay() 來代替 Handler 使用。

但用多了,總有點(diǎn)心虛,View.post() 會(huì)不會(huì)有什么隱藏的問題?所以趁有點(diǎn)空余時(shí)間,這段時(shí)間就來梳理一下,View.post() 原理到底是什么,內(nèi)部都做了啥事。

提問

開始看源碼前,先提幾個(gè)問題,帶著問題去看源碼應(yīng)該會(huì)比較有效率,防止閱讀源碼過程中,陷得太深,跟得太偏了。

Q1: 為什么 View.post() 的操作是可以對(duì) UI 進(jìn)行操作的呢,即使是在子線程中調(diào)用 View.post()?

Q2:網(wǎng)上都說 View.post() 中的操作執(zhí)行時(shí),View 的寬高已經(jīng)計(jì)算完畢,所以經(jīng)??匆娫?Activity 的 onCreate() 里調(diào)用 View.post() 來解決獲取 View 寬高為0的問題,為什么可以這樣做呢?

Q3:用 View.postDelay() 有可能導(dǎo)致內(nèi)存泄漏么?

ps:本篇分析的源碼基于 andoird-25 版本,版本不一樣源碼可能有些區(qū)別,大伙自己過源碼時(shí)可以注意一下。另,下面分析過程有點(diǎn)長(zhǎng),慢慢看哈。

源碼分析

好了,就帶著這幾個(gè)問題來跟著源碼走吧。其實(shí),這些問題大伙心里應(yīng)該都有數(shù)了,看源碼也就是為了驗(yàn)證心里的想法。第一個(gè)問題,之所以可以對(duì) UI 進(jìn)行操作,那內(nèi)部肯定也是通過 Handler 來實(shí)現(xiàn)了,所以看源碼的時(shí)候就可以看看內(nèi)部是如何對(duì) Handler 進(jìn)行封裝的。而至于剩下的問題,那就在看源碼過程中順帶看看能否找到答案吧。

View.post()

View.post() 方法很簡(jiǎn)單,代碼很少。那我們就一行行的來看。

如果 mAttachInfo 不為空,那就調(diào)用 mAttachInfo.mHanlder.post() 方法,如果為空,則調(diào)用 getRunQueue().post() 方法。

那就找一下,mAttachInfo 是什么時(shí)候賦值的,可以借助 AS 的 Ctrl + F 查找功能,過濾一下 mAttachInfo =,注意 = 號(hào)后面還有一個(gè)空格,否則你查找的時(shí)候會(huì)發(fā)現(xiàn)全文有兩百多處匹配到。我們只關(guān)注它是什么時(shí)候賦值的,使用的場(chǎng)景就不管了,所以過濾條件可以細(xì)一點(diǎn)。這樣一來,全文就只有兩處匹配:

一處賦值,一處置空,剛好又是在對(duì)應(yīng)的一個(gè)生命周期里:

dispatchAttachedToWindow() 下文簡(jiǎn)稱 attachedToWindow

dispatchDetachedFromWindow() 下文簡(jiǎn)稱 detachedFromWindow。

所以,如果 mAttachInfo 不為空的時(shí)候,走的就是 Handler 的 post(),也就是 View.post() 在這種場(chǎng)景下,實(shí)際上就是調(diào)用的 Handler.post(),接下去就是搞清楚一點(diǎn),這個(gè) Handler 是哪里的 Handler,在哪里初始化等等,但這點(diǎn)可以先暫時(shí)放一邊,因?yàn)?mAttachInfo 是在 attachedToWindow 時(shí)才賦值的,所以接下去關(guān)鍵的一點(diǎn)是搞懂 attachedToWindow 到 detachedFromWindow 這個(gè)生命周期分別在什么時(shí)候在哪里被調(diào)用了。

雖然我們現(xiàn)在還不清楚,attachedToWindow 到底是什么時(shí)候被調(diào)用的,但看到這里我們至少清楚一點(diǎn),在 Activity 的 onCreate() 期間,這個(gè) View 的 attachedToWindow 應(yīng)該是還沒有被調(diào)用,也就是 mAttachInfo 這時(shí)候還是為空,但我們?cè)?onCreate() 里執(zhí)行 View.post() 里的操作仍然可以保證是在 View 寬高計(jì)算完畢的,也就是開頭的問題 Q2,那么這點(diǎn)的原理顯然就是在另一個(gè) return 那邊的方法里了:getRunQueue().post()。

那么,我們就先解決 Q2 吧,為什么 View.post() 可以保證操作是在 View 寬高計(jì)算完畢之后呢?跟進(jìn) getRunQueue() 看看:

getRunQueue().post()

所以調(diào)用的其實(shí)是 HandlerActionQueue.post() 方法,那么我們?cè)倮^續(xù)跟進(jìn)去看看:

post(Runnable) 方法內(nèi)部調(diào)用了 postDelayed(Runnable, long),postDelayed() 內(nèi)部則是將 Runnable 和 long 作為參數(shù)創(chuàng)建一個(gè) HandlerAction 對(duì)象,然后添加到 mActions 數(shù)組里。下面先看看 HandlerAction:

很簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu),就一個(gè) Runnable 成員變量和一個(gè) long 成員變量。這個(gè)類作用可以理解為用于包裝 View.post(Runnable) 傳入的 Runnable 操作的,當(dāng)然因?yàn)檫€有 View.postDelay() ,所以就還需要一個(gè) long 類型的變量來保存延遲的時(shí)間了,這樣一來這個(gè)數(shù)據(jù)結(jié)構(gòu)就不難理解了吧。

所以,我們調(diào)用 View.post(Runnable) 傳進(jìn)去的 Runnable 操作,在傳到 HandlerActionQueue 里會(huì)先經(jīng)過 HandlerAction 包裝一下,然后再緩存起來。至于緩存的原理,HandlerActionQueue 是通過一個(gè)默認(rèn)大小為4的數(shù)組保存這些 Runnable 操作的,當(dāng)然,如果數(shù)組不夠用時(shí),就會(huì)通過 GrowingArrayUtils 來擴(kuò)充數(shù)組,具體算法就不繼續(xù)看下去了,不然越來越偏。

到這里,我們先來梳理下:

當(dāng)我們?cè)?Activity 的 onCreate() 里執(zhí)行 View.post(Runnable) 時(shí),因?yàn)檫@時(shí)候 View 還沒有 attachedToWindow,所以這些 Runnable 操作其實(shí)并沒有被執(zhí)行,而是先通過 HandlerActionQueue 緩存起來。

那么到什么時(shí)候這些 Runnable 才會(huì)被執(zhí)行呢?我們可以看看 HandlerActionQueue 這個(gè)類,它的代碼不多,里面有個(gè) executeActions() 方法,看命名就知道,這方法是用來執(zhí)行這些被緩存起來的 Runnable 操作的:

哇,看到重量級(jí)的人物了:Handler??磥肀痪彺嫫饋頉]有執(zhí)行的 Runnable 最后也還是通過 Hnadler 來執(zhí)行的。那么,這個(gè) Handler 又是哪里的呢?看來關(guān)鍵點(diǎn)還是這個(gè)方法在哪里被調(diào)用了,那就找找看:

借助 AS 的 Ctrl + Alt + F7 快捷鍵,可以查找 SDK 里的某個(gè)方法在哪些地方被調(diào)用了。

很好,找到了,而且只找到這個(gè)地方。其實(shí),這個(gè)快捷鍵有時(shí)并沒有辦法找到一些方法被調(diào)用的地方,這也是源碼閱讀過程中令人頭疼的一點(diǎn),因?yàn)闆]法找到這些方法到底在哪些地方被調(diào)用了,所以很難把流程梳理下來。如果方法是私有的,那很好辦,就用 Ctrl + F 在這個(gè)類里找一下就可以,如果匹配結(jié)果太多,那就像開頭那樣把過濾條件詳細(xì)一點(diǎn)。如果方法不是私有的,那真的就很難辦了,這也是一開始找到 dispatchAttachedToWindow() 后為什么不繼續(xù)跟蹤下去轉(zhuǎn)而來分析Q2:getRunQueue() 的原因,因?yàn)橛?AS 找不到 dispatchAttachedToWindow() 到底在哪些地方被誰(shuí)調(diào)用了。哇,好像又扯遠(yuǎn)了,回歸正題回歸正題。

emmm,看來這里也繞回來了,dispatchAttachedToWindow() 看來是個(gè)關(guān)鍵的節(jié)點(diǎn)。

那到這里,我們?cè)俅蝸硎崂硪幌拢?/p>

我們使用 View.post() 時(shí),其實(shí)內(nèi)部它自己分了兩種情況處理,當(dāng) View 還沒有 attachedToWindow 時(shí),通過 View.post(Runnable) 傳進(jìn)來的 Runnable 操作都先被緩存在 HandlerActionQueue,然后等 View 的 dispatchAttachedToWindow() 被調(diào)用時(shí),就通過 mAttachInfo.mHandler 來執(zhí)行這些被緩存起來的 Runnable 操作。從這以后到 View 被 detachedFromWindow 這段期間,如果再次調(diào)用 View.post(Runnable) 的話,那么這些 Runnable 不用再緩存了,而是直接交給 mAttachInfo.mHanlder 來執(zhí)行。

以上,就是到目前我們所能得知的信息。這樣一來,Q2 是不是漸漸有一些頭緒了:View.post(Runnable) 的操作之所以可以保證肯定是在 View 寬高計(jì)算完畢之后才執(zhí)行的,是因?yàn)檫@些 Runnable 操作只有在 View 的 attachedToWindow 到 detachedFromWiondow 這期間才會(huì)被執(zhí)行。

那么,接下去就還剩兩個(gè)關(guān)鍵點(diǎn)需要搞清楚了:

dispatchAttachedToWindow() 是什么時(shí)候被調(diào)用的? mAttachInfo 是在哪里初始化的? dispatchAttachedToWindow() & mAttachInfo

只借助 AS 的話,很難找到 dispatchAttachedToWindow() 到底在哪些地方被調(diào)用。所以,到這里,我又借助了 Source Insight 軟件。

很棒!找到了四個(gè)被調(diào)用的地方,三個(gè)在 ViewGroup 里,一個(gè)在 ViewRootImpl.performTraversals() 里。找到了就好,接下去繼續(xù)用 AS 來分析吧,Source Insight 用不習(xí)慣,不過分析源碼時(shí)確實(shí)可以結(jié)合這兩個(gè)軟件。

哇,懵逼,完全懵逼。我就想看個(gè) View.post(),結(jié)果跟著跟著,跟到這里來了。ViewRootImpl 我在分析Android KeyEvent 點(diǎn)擊事件分發(fā)處理流程時(shí)短暫接觸過,但這次顯然比上次還需要更深入去接觸,哎,力不從心啊。

我只能跟大伙肯定的是,mView 是 Activity 的 DecorView。咦~,等等,這樣看來 ViewRootImpl 是調(diào)用的 DecorView 的 dispatchAttachedToWindow() ,但我們?cè)谑褂?View.post() 時(shí),這個(gè) View 可以是任意 View,并不是非得用 DecorView 吧。哈哈哈,這是不是代表著我們找錯(cuò)地方了?不管了,我們就去其他三個(gè)被調(diào)用的地方: ViewGroup 里看看吧:

addViewInner() 是 ViewGroup 在添加子 View 時(shí)的內(nèi)部邏輯,也就是說當(dāng) ViewGroup addView() 時(shí),如果 mAttachInfo 不為空,就都會(huì)去調(diào)用子 View 的 dispatchAttachedToWindow(),并將自己的 mAttachInfo 傳進(jìn)去。還記得 View 的 dispatchAttachedToWindow() 這個(gè)方法么:

mAttachInfo 唯一被賦值的地方也就是在這里,那么也就是說,子 View 的 mAttachInfo 其實(shí)跟父控件 ViewGroup 里的 mAttachInfo 是同一個(gè)的。那么,關(guān)鍵點(diǎn)還是這個(gè) mAttachInfo 什么時(shí)候才不為空,也就是說 ViewGroup 在 addViewInner() 時(shí),傳進(jìn)去的 mAttachInfo 是在哪被賦值的呢?我們來找找看:

咦,利用 AS 的 Ctrl + 左鍵 怎么找不到 mAttachInfo 被定義的地方呢,不管了,那我們用 Ctrl + F 搜索一下在 ViewGroup 類里 mAttachInfo 被賦值的地方好了:

咦,怎么一個(gè)地方也沒有。難道說,這個(gè) mAttachInfo 是父類 View 定義的變量么,既然 AS 找不到,我們換 Source Insight 試試:

還真的是,ViewGroup 是繼承的 View,并且處于同一個(gè)包里,所以可以直接使用該變量,那這樣一來,我們豈不是又繞回來了。前面說過,dispatchAttachedToWindow() 在 ViewGroup 里有三處調(diào)用的地方,既然 addViewInner() 這里的看不出什么,那去另外兩個(gè)地方看看:

剩下的兩個(gè)地方就都是在 ViewGroup 重寫的 dispatchAttachedToWindow() 方法里了,這代碼也很好理解,在該方法被調(diào)用的時(shí)候,先執(zhí)行 super 也就是 View 的 dispatchAttachedToWindow() 方法,還沒忘記吧,mAttachInfo 就是在這里被賦值的。然后再遍歷子 View,分別調(diào)用子 View 的 dispatchAttachedToWindow() 方法,并將 mAttachInfo 作為參數(shù)傳遞進(jìn)去,這樣一來,子 View 的 mAttachInfo 也都被賦值了。

但這樣一來,我們就繞進(jìn)死胡同了。

我們還是先來梳理一下吧:

目前,我們知道,View.post(Runnable) 的這些 Runnable 操作,在 View 被 attachedToWindow 之前會(huì)先緩存下來,然后在 dispatchAttachedToWindow() 被調(diào)用時(shí),就將這些緩存下來的 Runnable 通過 mAttachInfo 的 mHandler 來執(zhí)行。在這之后再調(diào)用 View.post(Runnable) 的話,這些 Runnable 操作就不用再被緩存了,而是直接交由 mAttachInfo 的 mHandler 來執(zhí)行。

所以,我們得搞清楚 dispatchAttachedToWindow() 在什么時(shí)候被調(diào)用,以及 mAttachInfo 是在哪被初始化的,因?yàn)樾枰浪淖兞咳?mHandler 都是些什么以及驗(yàn)證 mHandler 執(zhí)行這些 Runnable 操作是在 measure 之后的,這樣才能保證此時(shí)的寬高不為0。

然后,我們?cè)诟?dispatchAttachedToWindow() 被調(diào)用的地方時(shí),跟到了 ViewGroup 的 addViewInner() 里。在這里我們得到的信息是如果 mAttachInfo 不為空時(shí),會(huì)直接調(diào)用子 View 的 dispatchAttachedToWindow(),這樣新 add 進(jìn)來的子 View 的 mAttachInfo 就會(huì)被賦值了。但 ViewGroup 的 mAttachInfo 是父類 View 的變量,所以為不為空的關(guān)鍵還是回到了 dispatchAttachedToWindow() 被調(diào)用的時(shí)機(jī)。

我們還跟到了 ViewGroup 重寫的 dispatchAttachedToWindow() 方法里,但顯然,ViewGroup 重寫這個(gè)方法只是為了將 attachedToWindow 這個(gè)事件通知給它所有的子 View。

所以,最后,我們能得到的結(jié)論就是,我們還得再回去 ViewRootImpl 里,dispatchAttachedToWindow() 被調(diào)用的地方,除了 ViewRootImpl,我們都分析過了,得不到什么信息,只剩最后 ViewRootImpl 這里了,所以關(guān)鍵點(diǎn)肯定在這里??磥磉@次,不行也得上了。

ViewRootImpl.performTraversals()

這方法代碼有八百多行??!不過,我們只關(guān)注我們需要的點(diǎn)就行,這樣一省略無關(guān)代碼來看,是不是感覺代碼就簡(jiǎn)單得多了。

mFirst 初始化為 true,全文只有一處賦值,所以 if(mFirst) 塊里的代碼只會(huì)執(zhí)行一次。我對(duì) ViewRootImpl 不是很懂,performTraversals() 這個(gè)方法應(yīng)該是通知 Activity 的 View 樹開始測(cè)量、布局、繪制。而 DevorView 是 Activity 視圖的根布局、View 樹的起點(diǎn),它繼承 FrameLayout,所以也是個(gè) ViewGroup,而我們之前對(duì) ViewGroup 的 dispatchAttachedToWindow() 分析過了吧,在這個(gè)方法里會(huì)將 mAttachInfo 傳給所有子 View。也就是說,在 Activity 首次進(jìn)行 View 樹的遍歷繪制時(shí),ViewRootImpl 會(huì)將自己的 mAttachInfo 通過根布局 DecorView 傳遞給所有的子 View 。

那么,我們就來看看 ViewRootImpl 的 mAttachInfo 什么時(shí)候初始化的吧:

在構(gòu)造函數(shù)里對(duì) mAttachInfo 進(jìn)行初始化,傳入了很多參數(shù),我們關(guān)注的應(yīng)該是 mHandler 這個(gè)變量,所以看看這個(gè)變量定義:

終于找到 new Handler() 的地方了,至于這個(gè)自定義的 Handler 類做了啥,我們不關(guān)心,反正通過 post() 方式執(zhí)行的操作跟它自定義的東西也沒有多大關(guān)系。我們關(guān)心的是在哪 new 了這個(gè) Handler。因?yàn)槊總€(gè) Handler 在 new 的時(shí)候都會(huì)綁定一個(gè) Looper,這里 new 的時(shí)候是無參構(gòu)造函數(shù),那默認(rèn)綁定的就是當(dāng)前線程的 Looper,而這句 new 代碼是在主線程中執(zhí)行的,所以這個(gè) Handler 綁定的也就是主線程的 Looper。至于這些的原理,就涉及到 Handler 的源碼和 ThreadLocal 的原理了,就不繼續(xù)跟進(jìn)了,太偏了,大伙清楚結(jié)論這點(diǎn)就好。

這也就是為什么 View.post(Runnable) 的操作可以更新 UI 的原因,因?yàn)檫@些 Runnable 操作都通過 ViewRootImpl 的 mHandler 切到主線程來執(zhí)行了。

這樣 Q1 就搞定了,終于搞定了一個(gè)問題,不容易啊,本來以為很簡(jiǎn)單的來著。

跟到 ViewRootImpl 這里應(yīng)該就可以停住了。至于 ViewRootImpl 跟 Activity 有什么關(guān)系、什么時(shí)候被實(shí)例化的、跟 DecroView 如何綁定的就不跟進(jìn)了,因?yàn)槲乙策€不是很懂,感興趣的可以自己去看看,我在末尾會(huì)給一些參考博客。

至此,我們清楚了 mAttachInfo 的由來,也知道了 mAttachInfo.mHandler,還知道在 Activity 首次遍歷 View 樹進(jìn)行測(cè)量、繪制時(shí)會(huì)通過 DecorView 的 dispatchAttachedToWindow() 將 ViewRootImpl 的 mAttachInfo 傳遞給所有子 View,并通知所有調(diào)用 View.post(Runnable) 被緩存起來的 Runnable 操作可以執(zhí)行了。

但不知道大伙會(huì)不會(huì)跟我一樣還有一點(diǎn)疑問:看網(wǎng)上對(duì) ViewRootImpl.performTraversals() 的分析:遍歷 View 樹進(jìn)行測(cè)量、布局、繪制操作的代碼顯然是在調(diào)用了 dispatchAttachedToWindow() 之后才執(zhí)行,那這樣一來是如何保證 View.post(Runnable) 的 Runnable 操作可以獲取到 View 的寬高呢?明明測(cè)量的代碼 performMeasure() 是在 dispatchAttachedToWindow() 后面才執(zhí)行。

我在這里卡了很久,一直沒想明白。我甚至以為是 PhoneWindow 在加載 layout 布局到 DecorView 時(shí)就進(jìn)行了測(cè)量的操作,所以一直跟,跟到 LayoutInflater.inflate(),跟到了 ViewGroup.addView(),最后發(fā)現(xiàn)跟測(cè)量有關(guān)的操作最終都又繞回到 ViewRootImpl 中去了。

原來是自己火候不夠,對(duì) Android 的消息機(jī)制還不大理解,這篇博客前前后后寫了一兩個(gè)禮拜,就是在不斷查缺補(bǔ)漏,學(xué)習(xí)、理解相關(guān)的知識(shí)點(diǎn)。

大概的來講,就是我們的 app 都是基于消息驅(qū)動(dòng)機(jī)制來運(yùn)行的,主線程的 Looper 會(huì)無限的循環(huán),不斷的從 MessageQueue 里取出 Message 來執(zhí)行,當(dāng)一個(gè) Message 執(zhí)行完后才會(huì)去取下一個(gè) Message 來執(zhí)行。而 Handler 則是用于將 Message 發(fā)送到 MessageQueue 里,等輪到 Message 執(zhí)行時(shí),又通過 Handler 發(fā)送到 Target 去執(zhí)行,等執(zhí)行完再取下一個(gè) Message,如此循環(huán)下去。

清楚了這點(diǎn)后,我們?cè)倩剡^頭來看看:

performTraversals() 會(huì)先執(zhí)行 dispatchAttachedToWindow(),這時(shí)候所有子 View 通過 View.post(Runnable) 緩存起來的 Runnable 操作就都會(huì)通過 mAttachInfo.mHandler 的 post() 方法將這些 Runnable 封裝到 Message 里發(fā)送到 MessageQueue 里。mHandler 我們上面也分析過了,綁定的是主線程的 Looper,所以這些 Runnable 其實(shí)都是發(fā)送到主線程的 MessageQueue 里排隊(duì),等待執(zhí)行。然后 performTraversals() 繼續(xù)往下工作,相繼執(zhí)行 performMeasure(),performLayout() 等操作。等全部執(zhí)行完后,表示這個(gè) Message 已經(jīng)處理完畢,所以 Looper 才會(huì)去取下一個(gè) Message,這時(shí)候,才有可能輪到這些 Runnable 執(zhí)行。所以,這些 Runnable 操作也就肯定會(huì)在 performMeasure() 操作之后才執(zhí)行,寬高也就可以獲取到了。畫張圖,幫助理解一下:

哇,Q2的問題終于也搞定了,也不容易啊。本篇也算是結(jié)束了。

總結(jié)

分析了半天,最后我們來稍微小結(jié)一下:

View.post(Runnable) 內(nèi)部會(huì)自動(dòng)分兩種情況處理,當(dāng) View 還沒 attachedToWindow 時(shí),會(huì)先將這些 Runnable 操作緩存下來;否則就直接通過 mAttachInfo.mHandler 將這些 Runnable 操作 post 到主線程的 MessageQueue 中等待執(zhí)行。

如果 View.post(Runnable) 的 Runnable 操作被緩存下來了,那么這些操作將會(huì)在 dispatchAttachedToWindow() 被回調(diào)時(shí),通過 mAttachInfo.mHandler.post() 發(fā)送到主線程的 MessageQueue 中等待執(zhí)行。

mAttachInfo 是 ViewRootImpl 的成員變量,在構(gòu)造函數(shù)中初始化,Activity View 樹里所有的子 View 中的 mAttachInfo 都是 ViewRootImpl.mAttachInfo 的引用。

mAttachInfo.mHandler 也是 ViewRootImpl 中的成員變量,在聲明時(shí)就初始化了,所以這個(gè) mHandler 綁定的是主線程的 Looper,所以 View.post() 的操作都會(huì)發(fā)送到主線程中執(zhí)行,那么也就支持 UI 操作了。

dispatchAttachedToWindow() 被調(diào)用的時(shí)機(jī)是在 ViewRootImol 的 performTraversals() 中,該方法會(huì)進(jìn)行 View 樹的測(cè)量、布局、繪制三大流程的操作。

Handler 消息機(jī)制通常情況下是一個(gè) Message 執(zhí)行完后才去取下一個(gè) Message 來執(zhí)行(異步 Message 還沒接觸),所以 View.post(Runnable) 中的 Runnable 操作肯定會(huì)在 performMeaure() 之后才執(zhí)行,所以此時(shí)可以獲取到 View 的寬高。

好了,就到這里了。至于開頭所提的問題,前兩個(gè)已經(jīng)在上面的分析過程以及總結(jié)里都解答了。而至于剩下的問題,這里就稍微提一下:

使用 View.post(),還是有可能會(huì)造成內(nèi)存泄漏的,Handler 會(huì)造成內(nèi)存泄漏的原因是由于內(nèi)部類持有外部的引用,如果任務(wù)是延遲的,就會(huì)造成外部類無法被回收。而根據(jù)我們的分析,mAttachInfo.mHandler 只是 ViewRootImpl 一個(gè)內(nèi)部類的實(shí)例,所以使用不當(dāng)還是有可能會(huì)造成內(nèi)存泄漏的。

相關(guān)文章

最新評(píng)論