衆所周知,微信朋友圈的列表一直以來被衆多研究性能問題的朋友拿來做爲模範,對於其實現方式,一直以來有點難以望其項背的感受。只能默默的感嘆微信的開發者是真的牛逼。通過一段時間的研究,如今我將帶領你們以全新的認知對微信朋友圈的結構進行分析,並經過本身的方式加以實現。 先上圖:git
GIF看着有點卡,能夠下載apk自行體驗,流暢度和微信幾乎無差異:app-debug.apkgithub
源碼地址:HighPerformanceFriendsCircleapi
咱們都知道,在Android中,對於列表的而言,要避免其卡頓,能夠從如下幾個角度進行優化。數組
一、減小布局層級,避免過多的Item View的無用佈局嵌套。 二、對於有圖片的列表,要在滑動時對圖片加以控制,即滑動時不加載圖片,中止滑動以後再加載圖片。 三、應當避免在Adapter的填充數據時作過多的計算,或者嵌套過多的邏輯判斷。對於複雜的計算結果應當在Adapter填充數據以前計算完成。緩存
以上這些都是針對一個普通的Adapter所基本的一些優化,而對於微信朋友圈這種複雜列表,除了以上幾種以外,還須要對其進行其餘方面的優化。例如包括減小View的重複建立,構建緩存View,以及減小布局的onMeasure和onLayout次數。這些都尤其重要。下面咱們先簡單分析一些微信的列表每一項的視圖結構,經過分析微信,咱們能夠參悟到一些本身的解決思路。微信
首先咱們經過Android Device Monitor視圖分析器來分析微信朋友圈的每個Item的視圖結構。 app
在ViewGroup中,有個一方法可能一直被你們所忽略,它就是addViewInLayout(),因爲它是protected聲明的,因此外部的ViewGroup的子類沒法直接調用,而要使用addViewInLayout(),必須繼承ViewGroup或ViewGroup的子類並重寫該方法。那麼addViewInLayout()方法究竟有何用呢?它和咱們經常使用的addView()又有什麼區別呢?佈局
咱們先來看官方對addViewInLayout()的解釋: 性能
簡單翻譯:在佈局中添加視圖。 若是在onLayout()方法中,您須要添加更多視圖(例如列表視圖),這很是有用。 若是索引是負數,則意味着將其放在列表的末尾。優化
這彷佛沒什麼特殊的,可是它真正的有用的地方在於該方法中的preventRequestLayout
參數,這是一個boolean類型的值,可是它確實及其有用的。若是爲true,在添加View時他將不會觸發子對象的佈局請求。也就是說添加View時不會觸發onMeasure和onLayout操做。官方api解釋圖:
經過對addViewInLayout()的分析,我想你大概明白了,既然動態添加View的時候能夠不用觸發onMeasure()和onLayout(),那將大量的節約adapter的刷新速度。上面咱們有提到過,對adapter的性能要點中,減小adapter Item的onMeasure和onLayout尤其重要(由於事實上View的顯示onMeasure和onLayout須要耗費大量的時間)。
一樣的,在移除View時,咱們可使用removeViewInLayout(),它有和addViewInLayout()同樣的效果。所以,經過這個辦法,咱們解決了評論列表的動態變化更新的性能問題。
而九宮格圖片的展現只須要自定義ViewGroup便可實現,其內部依然是對ImageView的添加和移除,一樣的咱們可使用該方法addViewInLayout()和removeViewInLayout()來減小onMeasure()和onLayout()的次數以節省性能開支。
其餘方面的優化則是儘管在數據Bean中完成對各類數據變換的操做,包括複雜的計算,好比將String轉換成須要的SpannableStringBuilder等。
最後就是除了要減小onMeasure()和onLayout()的次數以後,咱們也須要減小View的建立。減小View的建立咱們可使用一個弱引用的緩存數組和實現View對象的緩存,這裏要感謝razerdp提供的思路。
具體的一些其餘邏輯,代碼中自行研究吧,後續可能還會繼續更新該項目,包括表情的匹配,電話號碼的匹配等,看本身時間狀況。歡迎你們start!
昨天有一位網友提出了一個問題,後面通過分析,發如今adapter中使用addViewInLayout()和addView()去添加一個或多個View無異,也就是說在adapter中構建評論數據時使用addViewInLayout()並不會減小onMeasure()和onLayout()的次數,緣由後續再單獨出一篇文章進行說明,這裏感謝@Caij的指正!