公司App的相冊加載速度太慢了,之前提過一次,然後一個同僚就去做這個優化工作了,這兩天突然想起來了,就看一下他優化的效果,發現加載速度還是太慢。以我的HUAWEI ANE-LX2 Android 8.0.0 API 26測試機為例,8000多張圖檔首次加載時間大約4s左右,而且如果切換排序,這個時間也挺長,總之,我認為體驗效果非常不好,最好優化到1s左右。
我先看了下他優化的代碼,就是用了IntentService開一個線程去請求系統資料庫;然後cursor.moveToNext()讀取字段并構造我們的ImageInfo結構,添加到List清單(會同時維護All、Video、Image三個清單)中;周遊結束後,通過EventBus發送消息到主線程,然後調用RecyclerView的Adapter來添加資料更新界面。雖然将耗時任務放在了子線程,但是由于8000多條周遊及ImageInfo的建立,消耗了大量記憶體和時間。
現在分析下功能需求:我們的App是視訊編輯類App,使用者使用相冊不會選擇全部,或者說進入的時候不需要看到全部内容,是以隻需要加載一屏的圖檔資料就好了,其他的沒有顯示的部分,等使用者切換卡片或者下滑時候加載更多。換句話說,優化成recyclerview下拉加載更多樣式。
先說下我優化後的結果:保證功能效果的同時,從點選功能點到顯示清單,用時300多ms,和之前的4s左右相比,使用者體驗提升非常明顯。
優化步驟:
1、将All、Video、Image三個Fragment改為懶加載模式,具體方法google一下。
2、将RecyclerView改為分段加載模式。
3、分頁請求資料。由于每次查詢60條資料庫時間都在幾十毫秒,時間可以說是很及時,是以不對查詢的結果及建構的ImageInfo清單進行存儲,減少記憶體的長期占用。構造的查詢字段,參數是檔案夾名和修改時間(時間倒序排列),建構完ImageInfo資料後EventBus發送到指定Fragment頁面進行頁面更新處理。
4、預加載一定數量的RecyclerView子視圖。這個頁面,我們的設計是一排四個子視圖,每個子視圖都是正方形,那麼一屏的子視圖數就在(ScreenHeight/(ScreenWidth/4) + 2) * 4個(多建立一排,防止不夠用)。其實一個布局檔案inflate耗時也就35ms左右,但是頂不住數量多啊,我這個測試手機一次要建28個,那總時間就變成了35 * 28 = 980ms,加上其他時間的耗費,單個Fragment顯示的用時在1s多,是以把大量子視圖在子線程建立以免阻塞main線程非常有必要。
使用的時候就簡單判斷下:
按以上方法到這裡,你運作一下,其實就發現相冊加載性能提升已經非常明顯了,但是有個問題就是再次執行以上onCreateViewHolder時候會報異常:java.lang.IllegalStateException: ViewHolder views must not be attached when created. Ensure that you are not passing 'true' to the attachToRoot parameter of LayoutInflater.inflate(..., boolean attachToRoot)。看提示意思就是view已經有了一個parent了,再使用時候添加一個parent就會出錯,因為view隻能有一個直接parent視圖。那解決方案就是清理一下view的parent,你搜一下會發現view中沒有setParent的方法,有一個assignParent還是私有的,是以重置不了view的視圖的parent,那就沒法重複利用。好在突然想到view是在recyclerview中存在,在在fragment要銷毀時候,RecyclerView可以清理一下所有子視圖來達到重置view的parent的目的。看下RecyclerView的removeAllViewsInLayout源碼:
public voidremoveAllViewsInLayout() {
for(
inti = count - 1; i >= 0; i--) {
finalView view = children[i];
……
view.mParent = null; //重置view的parent。……
}
……
}
好了,優化到此已經結束,希望你的相冊進入時間也進入毫秒級。