視覺化 ListView 快取機制,手摸手帶你打通任督二脈

揪克發表於2018-01-07

專案地址:ListViewVisualization

簡介

本文不涉及 ListView 快取機制的原始碼探析,關於 ListView 的快取機制郭霖前輩的《Android ListView工作原理完全解析,帶你從原始碼的角度徹底理解》已經分析的很通徹了,同理網上也有很多文章了。本文不針對 ListView 的快取機制做介紹,對於這塊還不夠了解的朋友可以閱讀上方郭霖前輩的文章。另外再配上騰訊 Bugly 的圖:

這裡寫圖片描述

痛點

對於像 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
  • mActiveView:ArrayList 型別,被 layoutChildren() 用於快取螢幕上的 View。

文章中提及的部分名詞:專案中 ListView 使用了兩個 ViewType,也就是有兩種佈局,其中第一種筆者在文中提及到時命名為 Item1,第二種稱為 Item2

初始化

這裡寫圖片描述

mActiveViews:長度為4。在筆者的手機上初始化時螢幕上只能容下4個 View mScrapViews:長度為2。筆者設定了兩種 ViewType,所以需要有兩套快取 View 的 ArrayList mCurrentScrap/mScrapViews[0]:長度為0。此時快取區肯定是0,因為沒有滑動所以不存在快取 View 一說。 mScrapViews[1]:長度為0。這個就更不解釋啦。

觸發第一個快取

這裡寫圖片描述

當 『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,各位讀者可以試試。

螢幕內容數量最大化

螢幕繼續下滑啊下滑~

這裡寫圖片描述

螢幕下滑沒多少就會突然發現 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 繼續下滑時將會發生什麼情形?

這裡寫圖片描述

一切都在掌控之內。

繼續觸發

螢幕繼續下滑,直至『14』被移出螢幕——

這裡寫圖片描述

當『14』被移出後,mCurrentScrap/mScrapViews[0] 的 size 從百年不變的 1 變成了 2。為什麼?因為『13』 用不上了,新出來的 View 是 Item2 而不是 Item1,所以快取只能無奈的加加加,而不能被複用。我們不妨移動到全屏為 Item2 ——

這裡寫圖片描述

直至『17』移出螢幕,mCurrentScrap/mScrapViews[0] 的大小最終達到了 5。

總結

什麼玩意兒,剛進 Item2 區域就總結啦?就總結啦!因為純粹的 Item2 區域滑動和純粹的 Item1 區域滑動情況是一樣的;而 Item2 區域滑向 Item1 區域的情形與 Item1 區域滑向 Item2 區域的情形也是一樣的。所以本文授人以漁的任務就完成了,剩下的就是各位讀者實操捕魚的過程了。

相關文章