写点什么

RecyclerView- 的复用机制,Android 开发面试技能介绍

用户头像
Android架构
关注
发布于: 刚刚

View view = mViewCacheExtensiongetViewForPositionAndType(this, position, type);holder = getChildViewHolder(view);}}


if (holder == null) {// 4) 从 RecycledViewPool 获取 ViewHolderholder = getRecycledViewPool().getRecycledView(type);}


if (holder == null) {// 缓存全取过了,没有,那只好 create 一个咯 holder = mAdapter.createViewHolder(RecyclerView.this, type);}


// This is very ugly but the only place we can grab this information// 大半夜看到这句注释的时候笑出声!像极了我写出丑代码时的无奈。


// 在这后面有一些刷新 ViewHolder 信息的代码,放这很丑,但又只能放这,为了能走到这,前面有多少 if (holder == null)}


分析完复用的部分,接下来再看一下 ViewHolder 存入缓存的部分

缓存

所谓的缓存,就是看一下是怎么样往前面提到的四级缓存添加数据的


  • mAttachedScrap 和 mChangedScrap

  • mCachedViews

  • ViewCacheExtension 前面说了,这个的创建和缓存完全由开发者自己控制,系统未往这里添加数据

  • RecycledViewPool

mAttachedScrap 和 mChangedScrap

如果调用了 Adapter 的 notifyXXX 方法,会重新回调到 LayoutManager 的 onLayoutChildren 方法里面, 而在 onLayoutChildren 方法里面,会将屏幕上所有的 ViewHolder 回收到 mAttachedScrap 和 mChangedScrap。


// 调用链 LinearLayoutManager.onLayoutChildren(...)-> LayoutManager.detachAndScrapAttachedViews(recycler)-> LayoutManager.scrapOrRecycleView(..., view)-> Recycler.scrapView(view);


private void scrapOrRecycleView(Recycler recycler, int inde


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


x, View view) {final ViewHolder viewHolder = getChildViewHolderInt(view);if (viewHolder.isInvalid() && !viewHolder.isRemoved()&& !mRecyclerView.mAdapter.hasStableIds()) {// 后面会讲,缓存到 mCacheViews 和 RecyclerViewPoolrecycler.recycleViewHolderInternal(viewHolder);} else {// 缓存到 scraprecycler.scrapView(view);}}


void scrapView(View view) {final ViewHolder holder = getChildViewHolderInt(view);if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)|| !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {// 标记为移除或失效的 || 完全没有改变 || item 无动画或动画不复用 mAttachedScrap.add(holder);} else {mChangedScrap.add(holder);}}


其实还有一种情况会调用到 scrapView , 从 mHiddenViews 获得一个 ViewHolder 的话(发生在支持动画的操作),会先将这个 ViewHolder 从 mHiddenViews 数组里面移除,然后调用:


Recycler.tryGetViewHolderForPositionByDeadline(...)-> Recycler.getScrapOrHiddenOrCachedHolderForPosition(...)-> Recycler.scrapView(view)

mCacheViews 和 RecyclerViewPool

这两级缓存的代码都在 Recycler 的这个方法里:


void recycleViewHolderInternal(ViewHolder holder) {if (forceRecycle || holder.isRecyclable()) {if(mViewCacheMax > 0&& !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID| ViewHolder.FLAG_REMOVED| ViewHolder.FLAG_UPDATE| ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {int cachedViewSize = mCachedViews.size();if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {// 1. mCacheViews 满了,最早加入的不要了放 RecyclerViewPoolrecycleCachedViewAt(0);}mCachedViews.add(targetCacheIndex, holder);cached = true;}


if (!cached) {// 2. 不能放进 mCacheViews 的放 RecyclerViewPooladdViewHolderToRecycledViewPool(holder, true);}}


}


// Recycles a cached view and removes the view from the listvoid recycleCachedViewAt(int cachedViewIndex) {ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);addViewHolderToRecycledViewPool(viewHolder, true);mCachedViews.remove(cachedViewIndex);}


在这我们知道 recycleViewHolderInternal 会把 ViewHolder 缓存到 mCacheViews ,而不满足加到 mCacheViews 的会缓存到 RecycledViewPool 。那又是什么时候调用的 recycleViewHolderInternal 呢?有以下三种情况:


  1. 重新布局,主要是调用 Adapter.notifyDataSetChange 且 Adapter 的 hasStableIds 方法返回为 false 时调用。从这边也可以看出为什么一般情况下 notifyDataSetChange 效率比其它 notifyXXX 方法低(使用二级缓存及优先级更低的缓存 ),同时也知道了,如果我们设置 Adapter.setHasStableIds(ture) 以及其它相关需要的实现,则可以提高效率(使用一级缓存)

  2. 在复用时,从一级缓存里面获取到 ViewHolder,但是此时这个 ViewHolder 已经不符合一级缓存的特点了(比如 Position 失效了,跟 ViewType 对不齐),就会从一级缓存里面移除这个 ViewHolder,添加到这两级缓存里面

  3. 当调用 removeAnimatingView 方法时,如果当前 ViewHolder 被标记为 remove ,会调用 recycleViewHolderInternal 方法来回收对应的 ViewHolder。调用 removeAnimatingView 方法的时机表示当前的 ItemAnimator 已经做完了

总结

到这里,RecyclerView 的缓存复用机制就分析完了,总结一下:


  • RecyclerView 的缓存复用机制,主要是通过内部类 Recycler 来实现

  • Recycler 有 4 级缓存,每一级的缓存都有各自的作用,会按优先级使用。

  • ViewHolder 会从某一级缓存移至其它级别的缓存

  • mHideenViews 的存在是为了解决在动画期间进行复用的问题。

  • 缓存复用 ViewHolder 时会针对内部不同的状态 ( mFlags ) 进行相应的处理。


GitHub

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
RecyclerView-的复用机制,Android开发面试技能介绍