Android中View.post和Handler.post的關(guān)系
前言
View.post和Handler.post是Android開發(fā)中經(jīng)常使用到的兩個”post“方法,我們經(jīng)常通過前者去獲取一些View在運行時的渲染數(shù)據(jù),或者測量頁面的渲染時間。而后者則是Android的核心Handler的一個方法,它會向?qū)?yīng)線程的MessageQueue中插入一條Message,在未來的某個事件點得到執(zhí)行.....
為什么要拿這二者來比較?
首先,這二者的名字相同
其次,是View.post()的調(diào)用時機和整個View的繪制和渲染有著千絲萬縷的聯(lián)系。而這一切的基礎(chǔ),正是主線程的Handler.post(),理清這二者的關(guān)系,能夠加深我們對View渲染、繪制的流程的理解。
View的渲染起點
宏觀上來說,當(dāng)DecorView被”attach“到Window之上后,程序能夠收到系統(tǒng)分配給各個Activity的同步信號時,View就會開始渲染了,當(dāng)每個同步信號到來時,ChoreoGrapher將會派發(fā)出一個信號通知ViewRootImpl進行視圖的渲染,因此,從系統(tǒng)上來看,每次釋放的Vsync同步信號應(yīng)該是視圖繪制的起點。
從App端來說,當(dāng)ScheduleTravesals被調(diào)用時,會先向MessageQueue中插入一個消息屏障
,此時會阻隔其他的同步消息的通過,允許異步消息的進入。然后mChoreoGrapher,向MessageQueue中插入一個視圖更新的信號,最終會走到doTraversals()方法中,在該方法的執(zhí)行過程中,將會先取消掉同步屏障,然后緊接著執(zhí)行performTraversals()方法。顯然,消息屏障
的作用就是提升peformTraversals的優(yōu)先級,確保視圖的優(yōu)先繪制。
不難發(fā)現(xiàn),真正的進行渲染的起點是perfromTraversals()
方法:
View.post的執(zhí)行流程
View.post在不同版本的Android系統(tǒng)中,有著不同的實現(xiàn),在API24以前,View.post所做的是:當(dāng)View.post被調(diào)用時,直接向ViewRootImpl的mRunQueue中插入一個Runnable,然后在performTraversals()過程中,統(tǒng)一進行處理,這樣一來,View.post()就會按照View.post()的調(diào)用順序在”未來的某個時間點“進行執(zhí)行,這說明:在這一系列的Android版本中,View.post的執(zhí)行順序就是本身調(diào)用View.post()的順序
處理:這里的處理并非直接執(zhí)行Runnable,而是統(tǒng)一插入到主線程的MessageQueue中去執(zhí)行;
“未來的某個時間點”,這個未來的某個時間點指的是perfromTraversals()中將ViewRootImpl中mRunQueue中的所有Runnable插入到MessageQueue之后的某個時間點。必然在performTraversals()之后。
如上圖,必須得等到整個perfromTraversals方法體執(zhí)行完成(包括)后,才有可能執(zhí)行下一個Message(這里標(biāo)注為了Runnable),而perfromTraversals()方法體中,會順序地調(diào)用performMeasure()、performLayout()、performDraw()方法,這三個方法走完,意味著視圖已經(jīng)完成了渲染,此時的View.post()執(zhí)行,必然是能落在視圖創(chuàng)建之后
。
而API24及之后的版本中,View.post所做的事情發(fā)生了改變,當(dāng)View.post()調(diào)用時,Runnable被插入到View各自的mRunQueue當(dāng)中,也就是說,每個View都含有一個mRunQueue,當(dāng)performTraversals()中,也沒有統(tǒng)一處理了,而是根據(jù) performTraversals()->dispatchAttachedToWindows()
遞歸地調(diào)用到子View時,子View將自己的mRunQueue插入到主線程的MessageQueue,這意味著:在高版本的執(zhí)行過程中,View.post()的執(zhí)行順序是按照視圖被迭代到的順序。
不變的是View.post()執(zhí)行,必然是能落在視圖創(chuàng)建之后
,這也是為什么能夠調(diào)用View.post()來獲取一些屏幕上的View的數(shù)據(jù)的原因。
Handler.post()能像View.post()一樣獲取到寬、高數(shù)據(jù)嗎?
Activity為我們暴露了三個常用的生命周期函數(shù):onCreate()、onStart()、onResume()。通常我們對一些事件的監(jiān)聽、View的初始化設(shè)置都會在這三個生命周期函數(shù)中實現(xiàn),以最后執(zhí)行的onReumse()為例,我們在其中使用主線程的Handler.post()獲取一個視圖的數(shù)據(jù),
我們可以看看結(jié)果:
override fun onResume(){ super.onResume() Handler(Looper.getMainLooper()).post{ Log.d("getHeight",textView.height.toString()) } }
D/getHeight: 0
顯然,失敗了。
我們知道,一個新的Activity的創(chuàng)建初期,DecorView并不會直接就和Activity建立聯(lián)系,建立聯(lián)系的過程在handleResumeActivity()
當(dāng)中,此時的DecorView被attach到了Activity之上。但是,我們需要明確一點:一個View如果沒有和Activity建立聯(lián)系,那么它將收不到系統(tǒng)的同步信號,也就無法更新(更新也沒有意義,因為它沒有地方去顯示),我們看看handleResumeActiivty
的執(zhí)行方法體,可以發(fā)現(xiàn),先走了onResume()的回調(diào),再走了a.mDecor = decor這一步驟,上文我們提到,視圖更新的事件是以Message的形式,在MessageQueue中”排隊“的,如果我們在onResume()中插入一個消息去獲取渲染之后的寬高數(shù)據(jù),那么這時的MessageQueue大概是這樣:
當(dāng)前正在執(zhí)行的是黃色的Message,這是一個從ActivityThread.java中H類發(fā)出的調(diào)度方法,它將會調(diào)用到handleResumeActivity中的一系列方法,最終走到onResume這,我們使用Handler.post(),我們會發(fā)現(xiàn)消息被插在了黃色的Message之后,但是此時的a.mDecor = decor
還沒有執(zhí)行,更不可能已經(jīng)發(fā)生繪制了,這也就意味著壓根沒渲染,沒視圖,自然也沒數(shù)據(jù),完整的流程如下:
到此這篇關(guān)于Android中View.post和Handler.post的關(guān)系的文章就介紹到這了,更多相關(guān)View.post與Handler.post內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android高級xml布局之輸入框EditText設(shè)計
這篇文章主要為大家詳細(xì)介紹了Android高級xml布局之輸入框EditText設(shè)計,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-12-12Flutter沉浸式狀態(tài)欄/AppBar導(dǎo)航欄/仿咸魚底部凸起導(dǎo)航欄效果
這篇文章主要介紹了Flutter沉浸式狀態(tài)欄/AppBar導(dǎo)航欄/仿咸魚底部凸起導(dǎo)航欄效果,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-04-04零基礎(chǔ)學(xué)習(xí)教程之Linux下搭建android開發(fā)環(huán)境
這篇文章主要介紹了Linux下搭建android開發(fā)環(huán)境,特別適合零基礎(chǔ)的同學(xué)學(xué)習(xí),想要在Linux及ubuntu11.10下配置android4.0.3開發(fā)環(huán)境的朋友可以參考一下2015-12-12Android中使用ScrollView實現(xiàn)滑動到底部顯示加載更多
本文主要介紹了android利用ScrollView實現(xiàn)滑動到底部顯示加載更多的示例代碼。具有很好的參考價值。下面跟著小編一起來看下吧2017-04-04修改Android簽名證書keystore的密碼、別名alias以及別名密碼
這篇文章主要介紹了修改Android簽名證書keystore的密碼、別名alias以及別名密碼的相關(guān)資料,需要的朋友可以參考下2015-12-12Android開發(fā)中Activity屬性設(shè)置小結(jié)
Android應(yīng)用開發(fā)中會經(jīng)常遇到Activity組件的使用,下面就來講解下Activity組件。Activity的生命周期、通信方式和IntentFilter等內(nèi)容,并提供了一些日常開發(fā)中經(jīng)常用到的關(guān)于Activity的技巧和方法。通過本文,你可以進一步了接Android中Activity的運作方式。2015-05-05Android實現(xiàn)內(nèi)存中數(shù)據(jù)保存到sdcard的方法
這篇文章主要介紹了Android實現(xiàn)內(nèi)存中數(shù)據(jù)保存到sdcard的方法,涉及Android的文件讀寫與I/O操作相關(guān)技巧,需要的朋友可以參考下2016-01-01