android RecyclerView的一些優(yōu)化點(diǎn)介紹
1.RecycledPool的重用
場景以及使用:
多個RecyclerView出現(xiàn),并且他們的item布局結(jié)構(gòu)一致,這時候可以進(jìn)行重用。
在進(jìn)行RecyclerView的初始化設(shè)置時候進(jìn)行RecycledPool的設(shè)置。
//每個單元的視頻列表的RecycledPool private var mRecycledViewPool: RecyclerView.RecycledViewPool? = null unitVideoListContentRv.run { layoutManager = GridLayoutManager(itemView.context, 3) if (mRecycledViewPool != null) { setRecycledViewPool(mRecycledViewPool) } else { mRecycledViewPool = recycledViewPool } ........... }
重用前后的對比:
本次展示的是長列表中的item嵌套列表,進(jìn)行多item的加載然后上下滑動,同時檢測內(nèi)存的開銷占用。
列表的規(guī)模是13個item,每個item中有4個視頻item,規(guī)模不算特別大。
重用RecycledPool之前:
數(shù)據(jù)加載完畢之后,最后上下滑動的內(nèi)存趨于平穩(wěn)在48.4m
重用RecycledPool之后:
數(shù)據(jù)加載完畢之后,最后上下滑動的內(nèi)存趨于平穩(wěn)在40m。
對比總結(jié):
其實(shí)很明顯可以看到內(nèi)存開銷減少,而內(nèi)存開銷減少能提升列表的流暢度,效果是顯而易見的。這次的列表數(shù)據(jù)規(guī)模還不算大,后續(xù)如果規(guī)模增加到很大,那么對比將會更加明顯。
2.setHasFixedSize(boolean)的使用
方法的名字就表明了,設(shè)置是否有固定的尺寸,就是說RecyclerView是否有固定的尺寸,如果設(shè)置了true。那么會在以下的情景用到:
onMeasure---測量
如果設(shè)置了true,那么RecyclerView的mHasFixedSize變量為true。
@Override protected void onMeasure(int widthSpec, int heightSpec) { if (mLayout == null) { defaultOnMeasure(widthSpec, heightSpec); return; } //是否允許自動測量 if (mLayout.isAutoMeasureEnabled()) { ..... } else { if (mHasFixedSize) { //是否有固定的尺寸 mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec); return; } ......... } } // mLayout.onMeasure public void onMeasure(@NonNull Recycler recycler, @NonNull State state, int widthSpec, int heightSpec) { mRecyclerView.defaultOnMeasure(widthSpec, heightSpec); } void defaultOnMeasure(int widthSpec, int heightSpec) { //直接設(shè)置固定的寬度和高度,沒有進(jìn)行再次的測量 final int width = LayoutManager.chooseSize(widthSpec, getPaddingLeft() + getPaddingRight(), ViewCompat.getMinimumWidth(this)); final int height = LayoutManager.chooseSize(heightSpec, getPaddingTop() + getPaddingBottom(), ViewCompat.getMinimumHeight(this)); setMeasuredDimension(width, height); }
所以設(shè)置了這個值的時候,RecyclerView在測量的時候會有性能上的提升。
3.setHasStableIds(boolean)的使用
方法的名稱意思是設(shè)置是否有穩(wěn)定的id,設(shè)置了該值為true后,ViewHolder中的mHasStableIds就為true。
StableId有三種模式:NO_STABLE_IDS、ISOLATED_STABLE_IDS、SHARED_STABLE_IDS
RecyclerView在進(jìn)行Item的Remove,Insert,Change的時候會調(diào)用到。
如果設(shè)置了這個屬性,那么需要在Adapter中重寫getItemId(int position)方法。
這樣子在進(jìn)行列表的更新時候,Adapter會根據(jù)getItemId方法返回的long類型的id進(jìn)行判斷,決定當(dāng)前的item是否需要刷新。因此取代以往的全部刷新的情況,從而提高效率。
class Album{ String coverUrl; String title; } @Override public long getItemId(int position){ Album album = mListOfAlbums.get(position); //如果返回的id和上次不一樣,那就代表這個item發(fā)生了數(shù)據(jù)變化,則進(jìn)行刷新 //如果返回的id和上次一致,那么這個item就沒有改變,就無需刷新了。 return (album.coverUrl + album.title).hashcode(); }
4.ViewCacheExtension的使用
它是一個靜態(tài)抽象類,看類名就能大概知道view緩存擴(kuò)展,類中包括方法:
/** 返回一個能綁定到適配器position位置上的view * <p> * 此方法不應(yīng)該創(chuàng)建新的視圖。 相反,它期望返回一個已經(jīng)創(chuàng)建的View,該View可以針對給定的類型和位置重 新使用。 如果將視圖標(biāo)記為已忽略,則應(yīng)先調(diào)用{@link LayoutManager# stopIgnoringView(View)},然后再返回視圖。 * RecyclerView將在必要時將返回的View重新綁定到該位置 * * @param recycler The Recycler that can be used to bind the View * @param position The adapter position * @param type The type of the View, defined by adapter * @return 綁定到給定位置的View;如果沒有可重用的View,則為NULL * @see LayoutManager#ignoreView(View) */ public abstract View getViewForPositionAndType(@NonNull Recycler recycler, int position, int type);
該緩存為RecyclerView的第二級緩存,即如果開發(fā)者設(shè)置了該緩存,那么列表從CacheView中獲取不到holder,就會從ViewCacheExtension從獲取。
適用場景則為,列表有固定的數(shù)量條目和寬高,這樣子,列表初始化的時候就能直接從這級緩存拿到ViewHolder,不需要再創(chuàng)建ViewHolder,大大節(jié)省時間,提高效率。
5.預(yù)加載
預(yù)加載功能在RecyclerView中是默認(rèn)開啟的。
public boolean onTouchEvent(MotionEvent e) { switch (action) { case MotionEvent.ACTION_MOVE: { final int x = (int) (e.getX(index) + 0.5f); final int y = (int) (e.getY(index) + 0.5f); int dx = mLastTouchX - x; int dy = mLastTouchY - y; if (mScrollState == SCROLL_STATE_DRAGGING) { //處于拖動狀態(tài) ........ if (mGapWorker != null && (dx != 0 || dy != 0)) { //滑動距離不等于0, mGapWorker.postFromTraversal(this, dx, dy); //進(jìn)行預(yù)取任務(wù) } } } break; } } /** * 在當(dāng)前遍歷之后立即安排預(yù)取。 */ void postFromTraversal(RecyclerView recyclerView, int prefetchDx, int prefetchDy) { if (recyclerView.isAttachedToWindow()) { ........ //第一次觸發(fā)拖動的是否將該runnable提交到Mainhandler里面, //等待UI thread執(zhí)行完成再執(zhí)行預(yù)取任務(wù) if (mPostTimeNs == 0) { mPostTimeNs = recyclerView.getNanoTime();//獲取當(dāng)前時間,記錄改次任務(wù)的開始 recyclerView.post(this); //提交當(dāng)前任務(wù) } } //設(shè)置預(yù)加載的坐標(biāo) recyclerView.mPrefetchRegistry.setPrefetchVector(prefetchDx, prefetchDy); } /** * 獲取當(dāng)前系統(tǒng)的時間,單位為納秒 */ long getNanoTime() { if (ALLOW_THREAD_GAP_WORK) { //在Android5.0及以上的系統(tǒng)中 return System.nanoTime(); //返回正在運(yùn)行的Java虛擬機(jī)的高分辨率時間源的當(dāng)前值,以納秒為單位 } else { return 0; //5.0以下的系統(tǒng)直接返回0 } } /** 在L +上,使用RenderThread,UI線程在將一幀傳遞給RenderThread之后但在下一幀開始之前具有空閑時間。我們 在此窗口中安排預(yù)取工作。 */ static final boolean ALLOW_THREAD_GAP_WORK = Build.VERSION.SDK_INT >= 21;
我們可以看下預(yù)加載程序的Runnable的run方法實(shí)現(xiàn)了什么操作。
@Override public void run() { try { TraceCompat.beginSection(RecyclerView.TRACE_PREFETCH_TAG); //recyclerview嵌套的情況 if (mRecyclerViews.isEmpty()) { // abort - no work to do return; } //查詢最新的vsync,以便于我們預(yù)測下一個 //繪制時間在動畫和輸入的回調(diào)中未生效,所以在這里進(jìn)行vsync的查詢是安全的 final int size = mRecyclerViews.size(); long latestFrameVsyncMs = 0; //獲取RecyclerView最近一次開始RenderThread的時間 for (int i = 0; i < size; i++) { RecyclerView view = mRecyclerViews.get(i); if (view.getWindowVisibility() == View.VISIBLE) { latestFrameVsyncMs = Math.max(view.getDrawingTime(), latestFrameVsyncMs); } } if (latestFrameVsyncMs == 0) { //終止,沒有任何視圖可見,或者無法獲得最新的vsync用于估計下一個 return; } //計算下一幀的時間,等于最新一幀的時間加上幀間隔的時間 //事實(shí)上,這是預(yù)加載工作的最后期限時間,如果不能在這個時間之前完成,那就意味著預(yù)加載失敗 long nextFrameNs = TimeUnit.MILLISECONDS.toNanos(latestFrameVsyncMs) + mFrameIntervalNs; //進(jìn)行預(yù)加載 prefetch(nextFrameNs); // TODO: consider rescheduling self, if there's more work to do } finally { mPostTimeNs = 0; TraceCompat.endSection(); } }
void prefetch(long deadlineNs) { //建立任務(wù)列表 buildTaskList(); //在deadlineNs這個時間前執(zhí)行并完成任務(wù) flushTasksWithDeadline(deadlineNs); } private void flushTasksWithDeadline(long deadlineNs) { for (int i = 0; i < mTasks.size(); i++) { final Task task = mTasks.get(i); if (task.view == null) { break; // done with populated tasks } flushTaskWithDeadline(task, deadlineNs); task.clear(); } }
6.更新列表的方式
item局部更新
單項(xiàng)item更新
notifyItemChanged(position)
notifyItemInserted(position)
notifyItemRemoved(position)
notifyItemMoved(fromPosition, toPosition)
整體列表更新
notifyDataSetChanged(慎用)
notifyItemRangeRemoved(positionStart, itemCount)
notifyItemRangeChanged(positionStart, itemCount)
notifyItemRangeInserted(positionStart, itemCount)
其它的優(yōu)化點(diǎn)
過度繪制
如果列表中的一個Item存在過度繪制,那么列表所有的item都過度繪制,就到存在不必要的渲染工作,消耗系統(tǒng)資源。
防止過度繪制,可以打開開發(fā)者選項(xiàng)中的《調(diào)試GPU過度繪制》,查看頁面中的顏色分區(qū),然后進(jìn)行對應(yīng)的優(yōu)化。
Android 將按如下方式為界面元素著色,以確定過度繪制的次數(shù):
真彩色:沒有過度繪制
藍(lán)色:過度繪制 1 次
綠色:過度繪制 2 次
粉色:過度繪制 3 次
紅色:過度繪制 4 次或更多次
因?yàn)樵诓季种型粠啻卫L制相同的像素就會發(fā)生繪制過度,因此修復(fù)過度繪制可以減少不必要的渲染工作,以此來提高性能。特別是對于大型,多列表的布局來說。
總結(jié)
到此這篇關(guān)于android RecyclerView的一些優(yōu)化點(diǎn)介紹的文章就介紹到這了,更多相關(guān)android RecyclerView內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android實(shí)現(xiàn)瘋狂連連看游戲之開發(fā)游戲界面(二)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)瘋狂連連看游戲之開發(fā)游戲界面,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-03-03Android開發(fā)Jetpack組件Lifecycle使用篇
這一篇文章來介紹Android?Jetpack架構(gòu)組件的Lifecycle;?Lifecycle用于幫助開發(fā)者管理Activity和Fragment?的生命周期,?由于Lifecycle是LiveData和ViewModel的基礎(chǔ);所以需要先學(xué)習(xí)它2022-08-08Android中AlertDialog 點(diǎn)擊按鈕后不關(guān)閉對話框的功能
本篇文章主要介紹了Android中AlertDialog 點(diǎn)擊按鈕后不關(guān)閉對話框的功能,非常具有實(shí)用價值,需要的朋友可以參考下2017-04-04android studio3.0.1無法啟動Gradle守護(hù)進(jìn)程的解決方法
這篇文章主要為大家詳細(xì)介紹了android studio3.0.1無法啟動Gradle守護(hù)進(jìn)程的解決方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-08-08Android簡單實(shí)用的可拖拽GridView組件分享
在我們?nèi)粘i_發(fā)中,使用?GridView?這種網(wǎng)格視圖的場合還是不少的,本篇我們來介紹一個支持拖拽的?GridView?組件,可以輕松搞定網(wǎng)格視圖的拖拽排序,需要的可以參考一下2023-06-06Android5.0+ CollapsingToolbarLayout使用詳解
這篇文章主要為大家詳細(xì)介紹了Android5.0+ CollapsingToolbarLayout使用,感興趣的小伙伴們可以參考一下2016-09-09詳細(xì)介紹Android-Room數(shù)據(jù)庫的使用
這篇文章主要介紹了詳細(xì)介紹Android-Room數(shù)據(jù)庫的使用,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-03-03