網站首頁 編程語言 正文
前言
View.post和Handler.post是Android開發中經常使用到的兩個”post“方法,我們經常通過前者去獲取一些View在運行時的渲染數據,或者測量頁面的渲染時間。而后者則是Android的核心Handler的一個方法,它會向對應線程的MessageQueue中插入一條Message,在未來的某個事件點得到執行.....
為什么要拿這二者來比較?
首先,這二者的名字相同
其次,是View.post()的調用時機和整個View的繪制和渲染有著千絲萬縷的聯系。而這一切的基礎,正是主線程的Handler.post(),理清這二者的關系,能夠加深我們對View渲染、繪制的流程的理解。
View的渲染起點
宏觀上來說,當DecorView被”attach“到Window之上后,程序能夠收到系統分配給各個Activity的同步信號時,View就會開始渲染了,當每個同步信號到來時,ChoreoGrapher將會派發出一個信號通知ViewRootImpl進行視圖的渲染,因此,從系統上來看,每次釋放的Vsync同步信號應該是視圖繪制的起點。
從App端來說,當ScheduleTravesals被調用時,會先向MessageQueue中插入一個消息屏障
,此時會阻隔其他的同步消息的通過,允許異步消息的進入。然后mChoreoGrapher,向MessageQueue中插入一個視圖更新的信號,最終會走到doTraversals()方法中,在該方法的執行過程中,將會先取消掉同步屏障,然后緊接著執行performTraversals()方法。顯然,消息屏障
的作用就是提升peformTraversals的優先級,確保視圖的優先繪制。
不難發現,真正的進行渲染的起點是perfromTraversals()
方法:
View.post的執行流程
View.post在不同版本的Android系統中,有著不同的實現,在API24以前,View.post所做的是:當View.post被調用時,直接向ViewRootImpl的mRunQueue中插入一個Runnable,然后在performTraversals()過程中,統一進行處理,這樣一來,View.post()就會按照View.post()的調用順序在”未來的某個時間點“進行執行,這說明:在這一系列的Android版本中,View.post的執行順序就是本身調用View.post()的順序
處理:這里的處理并非直接執行Runnable,而是統一插入到主線程的MessageQueue中去執行;
“未來的某個時間點”,這個未來的某個時間點指的是perfromTraversals()中將ViewRootImpl中mRunQueue中的所有Runnable插入到MessageQueue之后的某個時間點。必然在performTraversals()之后。
如上圖,必須得等到整個perfromTraversals方法體執行完成(包括)后,才有可能執行下一個Message(這里標注為了Runnable),而perfromTraversals()方法體中,會順序地調用performMeasure()、performLayout()、performDraw()方法,這三個方法走完,意味著視圖已經完成了渲染,此時的View.post()執行,必然是能落在視圖創建之后
。
而API24及之后的版本中,View.post所做的事情發生了改變,當View.post()調用時,Runnable被插入到View各自的mRunQueue當中,也就是說,每個View都含有一個mRunQueue,當performTraversals()中,也沒有統一處理了,而是根據?performTraversals()->dispatchAttachedToWindows()
遞歸地調用到子View時,子View將自己的mRunQueue插入到主線程的MessageQueue,這意味著:在高版本的執行過程中,View.post()的執行順序是按照視圖被迭代到的順序。
不變的是View.post()執行,必然是能落在視圖創建之后
,這也是為什么能夠調用View.post()來獲取一些屏幕上的View的數據的原因。
Handler.post()能像View.post()一樣獲取到寬、高數據嗎?
Activity為我們暴露了三個常用的生命周期函數:onCreate()、onStart()、onResume()。通常我們對一些事件的監聽、View的初始化設置都會在這三個生命周期函數中實現,以最后執行的onReumse()為例,我們在其中使用主線程的Handler.post()獲取一個視圖的數據,
我們可以看看結果:
override fun onResume(){ super.onResume() Handler(Looper.getMainLooper()).post{ Log.d("getHeight",textView.height.toString()) } }
D/getHeight: 0
顯然,失敗了。
我們知道,一個新的Activity的創建初期,DecorView并不會直接就和Activity建立聯系,建立聯系的過程在handleResumeActivity()
當中,此時的DecorView被attach到了Activity之上。但是,我們需要明確一點:一個View如果沒有和Activity建立聯系,那么它將收不到系統的同步信號,也就無法更新(更新也沒有意義,因為它沒有地方去顯示),我們看看handleResumeActiivty
的執行方法體,可以發現,先走了onResume()的回調,再走了a.mDecor = decor這一步驟,上文我們提到,視圖更新的事件是以Message的形式,在MessageQueue中”排隊“的,如果我們在onResume()中插入一個消息去獲取渲染之后的寬高數據,那么這時的MessageQueue大概是這樣:
當前正在執行的是黃色的Message,這是一個從ActivityThread.java中H類發出的調度方法,它將會調用到handleResumeActivity中的一系列方法,最終走到onResume這,我們使用Handler.post(),我們會發現消息被插在了黃色的Message之后,但是此時的a.mDecor = decor
還沒有執行,更不可能已經發生繪制了,這也就意味著壓根沒渲染,沒視圖,自然也沒數據,完整的流程如下:
原文鏈接:https://juejin.cn/post/6996222800725803038
相關推薦
- 2022-12-10 C語言中如何實現桶排序_C 語言
- 2022-12-16 C++?Futures與Promises線程使用示例講解_C 語言
- 2023-10-13 is using incorrect casing. Use PascalCase for Reac
- 2022-02-10 Linux環境下安裝docker環境(親測無坑)_docker
- 2022-05-20 flume的負載均衡load balancer
- 2023-05-26 C#?using()的使用方法_C#教程
- 2022-03-27 C++引用和指針的區別你知道嗎_C 語言
- 2022-08-07 Qt使用QPainter實現自定義圓形進度條_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支