簡介
本文不涉及 ListView 快取機制的原始碼探析,關於 ListView 的快取機制郭霖前輩的《Android ListView工作原理完全解析,帶你從原始碼的角度徹底理解》已經分析的很通徹了,同理網上也有很多文章了。本文不針對 ListView 的快取機制做介紹,對於這塊還不夠了解的朋友可以閱讀上方郭霖前輩的文章。另外再配上騰訊 Bugly 的圖:
![這裡寫圖片描述](https://i.iter01.com/images/9db60c1163d86173c60d817fed5ffd494d0c2859dbcb876270c45f5ca66674ee.jpg)
痛點
對於像 ListView/RecyclerView 這種級別 View 的原始碼是長篇且晦澀的,連郭霖前輩自己也說過 “沒過幾個月時間我就把當初梳理清晰的原始碼又忘的一乾二淨”
。且網上的文章都是針對程式碼闡述的,實在是有些難以理解,且部分知識點並未涉及(例如僅針對 ViewType 只有一種情況的情形做說明,多 ViewType 情形下快取機制少見闡述)。筆者遇到這些問題時候還是很頭疼的,於是就將 ListView 的快取機制給視覺化,再針對各個情形加以總結,相信能幫助到很多對 ListView 快取機制不太熟悉的讀者們。
快取機制解析
專案地址:ListViewVisualization或者你可以直接安裝:apk。 在手摸手解析之前,需要提及到 RecycleBin 中的幾個欄位 ,這些欄位在郭霖前輩的文章中基本都有所提及,實際上掌握了這些欄位在 ListView 快取機制中變換的情況,筆者認為對 ListView 的快取機制瞭解就算是比較通徹的了——
- mCurrentScrap:ArrayList
型別,用於儲存離屏的 View - mScrapViews
- ArrayList
[] 型別 - 陣列中每個元素都是 ArrayList
型別,效果同 mCurrentScrap - mScrapViews[0] 就是 mCurrentScrap
- 其陣列長度應為 ViewTypeCount。因為針對不同的 ViewType,ListView 都要有一個專門的 ArrayList
連結串列來快取它對應的 View
- ArrayList
- mActiveView:ArrayList
型別,被 layoutChildren() 用於快取螢幕上的 View。
文章中提及的部分名詞:專案中 ListView 使用了兩個 ViewType,也就是有兩種佈局,其中第一種筆者在文中提及到時命名為 Item1,第二種稱為 Item2。
初始化
![這裡寫圖片描述](https://i.iter01.com/images/9dea82963ea9378c75b8ab6d355122a5eb5134e4663c778cd2bfbf3709ace45e.png)
mActiveViews:長度為4。在筆者的手機上初始化時螢幕上只能容下4個 View
mScrapViews:長度為2。筆者設定了兩種 ViewType,所以需要有兩套快取 View 的 ArrayList
觸發第一個快取
![這裡寫圖片描述](https://i.iter01.com/images/631951424cb58f4f2fc6434ffb4e44b52a529d9adbddf7dd3bba946ea1e51dd2.gif)
當 『1』 被移出螢幕的時候,mCurrentScrap/mScrapViews[0] 就要動手將它快取起來啦,作為下一個進入螢幕的 View 的複用。在圖上此時也多出了一句 the last one of mCurrentScrap's type is ItemX---number
,為什麼要關注 mCurrentScrap 的最後一個值?因為 RecycleBin 在滑動區域都是 Item1 的情況下,取出快取的方式就是從末尾獲取一個 View,所以我們需要關注一下末尾 View 的型別,但是實際上在整個過程中該值的意義也並不大,純粹是為了展示離屏 Item 資訊。至於 ItemX 後面的數字,則是廢棄 View 中 TextView 中所顯示的 text 了。所以第一個快取是一個 Item1 型別的 TextView 顯示為 1 的 View 被移出螢幕了。
同理:當『2』被滑出螢幕的時候,『2』這個 View 將會被快取起來,此時螢幕上應該顯示了 the last one of mCurrentScrap's type is Item1---2
,各位讀者可以試試。
螢幕內容數量最大化
螢幕繼續下滑啊下滑~![這裡寫圖片描述](https://i.iter01.com/images/25a9e634207c66901828d60018ef7d27e19fa9c577189769bc927665f3fc91c2.gif)
螢幕下滑沒多少就會突然發現 mCurrentScrap/mScrapView[0] 為 0 啦,這說明此時 RecycleBin 中沒有快取!因為此時螢幕中已經顯示了 『2』~『6』共 5 個 View 了,之前的 『1』 已經被 『6』 複用了,所以不存在還有快取了。實際上針對於 ViewType 只有一種的情況來說,RecycleBin 機制中的 mCurrentScrap/mScrapViews[0] 的大小永遠是1。因為它至多隻需要快取一個 View,因為螢幕內容數量的最大值必定為初始時螢幕內容數量值 + 1,拿筆者的手機舉例來說,初始值為4,螢幕內容數量的最大值則為 5。
觸發 Item2 的顯示
筆者將 Item1 的數量設定為 17,當我們挪到 17 繼續下滑時將會發生什麼情形?![這裡寫圖片描述](https://i.iter01.com/images/4f250b7095a08612e118551aa86d41e5fbb7d4b5f8239cc8f9d5421282cdbfe5.gif)
一切都在掌控之內。
繼續觸發
螢幕繼續下滑,直至『14』被移出螢幕——![這裡寫圖片描述](https://i.iter01.com/images/2f7c6c4f37120c4303b986b2e08d0bec0738e92e74520ba49ef2b8af865c2693.gif)
當『14』被移出後,mCurrentScrap/mScrapViews[0] 的 size 從百年不變的 1 變成了 2。為什麼?因為『13』 用不上了,新出來的 View 是 Item2 而不是 Item1,所以快取只能無奈的加加加,而不能被複用。我們不妨移動到全屏為 Item2 ——
![這裡寫圖片描述](https://i.iter01.com/images/dea51f6b084ecb6df176a70d256c48fda00c058db7496b7692223fcb699d4e9a.gif)
直至『17』移出螢幕,mCurrentScrap/mScrapViews[0] 的大小最終達到了 5。