每日一问RecyclerView的多级缓存机制,每级缓存到底起到什么样的作用?

更新时间:2023-07-15 00:07:27 阅读: 评论:0

每⽇⼀问RecyclerView的多级缓存机制,每级缓存到底起到什
么样的作⽤?
谈到 RecyclerView,相信不少同学,张⼝都能说出它的⼏级缓存机制:
例如:
⼀级缓存:mAttachedScrap 和 mChangedScrap
⼆级缓存:mCachedViews
三级缓存:ViewCacheExtension
四级缓存:RecycledViewPool
然后说怎么⽤,就是先从 1 级找,然后 2 级...然后4 级,找不到 create ViewHolder。
那么,有没有思考过,其实上⾯⼏级缓存都属于“内存缓存",那么这么分级肯定有⼀定区别。
问题来了:
1. 每⼀级缓存具体作⽤是什么?
2. 分别在什么场景下会⽤到哪些缓存呢?
先来温习⼀下RecyclerView的滚动和回收机制:
RecyclerView之所以能滚动,就是因为它在监听到⼿指滑动之后,不断地更新Item的位置,也就是反复layout⼦View了,这部分⼯作由LayoutManager负责。
LayoutManager在layout⼦View之前,会先把RecyclerView的每个⼦View所对应的ViewHolder都放到mAttachedScrap中,然后根据当前滑动距离,筛选出哪些Item需要layout。获取⼦View对象,会通过getViewForPosition⽅法来获取。这个⽅法就是题⽬中说的那样:先
从mAttachedScrap中找,再......
Item布局完成之后,会对刚刚没有再次布局的Item进⾏缓存(回收),这个缓存分两种:
1. 取出(即将重⽤)时⽆须重新绑定数据(即不⽤执⾏onBindViewHolder⽅法)。这种缓存只适⽤于特定position的Item(名花有
济南新东方英语培训
主);
2. 取出后会回调onBindViewHolder⽅法,好让对应Item的内容能正确显⽰。这种缓存适⽤所有同类型的Item(云英未嫁);
第⼀种缓存,由Recycler.mCachedViews来保管,第⼆种放在RecycledViewPool中。
如果回收的Item它的状态(包括:INVALID、REMOVED、UPDATE、POSITION_UNKNOWN)没有变更,就会放
到mCachedViews中,否则扔RecycledViewPool⾥。
emmmm,除了滚动过程中,会对Item进⾏回收和重新布局,还有⼀种就是,当Adapter数据有更新时:
六级英语作文范文
1. Inrted:如果刚好插⼊在屏幕可见范围内,会从RecycledViewPool中找⼀个相同类型的ViewHolder(找不到就create)来重新绑
定数据并layout;
2. Removed:会把对应ViewHolder扔到mAttachedScrap中并播放动画,动画播放完毕后移到RecycledViewPool⾥;
3. Changed:这种情况并不是⼤家所认为的:直接将这个ViewHolder传到Adapter的onBindViewHolder中重新绑定数据。⽽是先把旧
的ViewHolder扔mChangedScrap中,然后像Inrted那样从RecycledViewPool中找⼀个相同类型的ViewHolder来重新绑定数据。
旧ViewHolder对象⽤来播放动画,动画播完,同样会移到RecycledViewPool⾥;
注意:如果是使⽤notifyDataSetChanged⽅法来通知更新的话,那么所有Item都会直接扔RecycledViewPool中,然后逐个重新绑定数据的。
当然了,上⾯说的这⼏种情况,不包括瞎写⾃定义的LayoutManager,因为在⾃定义的LayoutManger中,怎么去管理缓存,完全出于个⼈喜好。
好啦,温习完了之后,来看回题⽬中的问题:
这⼏个存放缓存的集合,各⾃的作⽤以及使⽤场景?
mAttachedScrap:LayoutManager每次layout⼦View之前,那些已经添加到RecyclerView中的Item以及被删除的Item的临时存放地。使⽤场景就是RecyclerView滚动时、还有在可见范围内删除Item后⽤notifyItemRemoved⽅法通知更新时;
mChangedScrap:作⽤:存放可见范围内有更新的Item。使⽤场景:可见范围内的Item有更新,并且使⽤notifyItemChanged⽅法通知更新时;就是临时缓存局部更新,⽤于播放动画,动画播放完viewholder还是会给 recyclerpool
mCachedViews:作⽤:存放滚动过程中没有被重新使⽤且状态⽆变化的那些旧Item。场景:滚动,prefetch;
实收资本
RecycledViewPool:作⽤:缓存Item的最终站,⽤于保存那些Removed、Changed、以及mCachedViews满了之后更旧的Item。场景:Item被移除、Item有更新、滚动过程;
写到这⾥发现漏讲了⼀个prefetch,好吧,这个prefetch机制就是RecyclerView在滚动和惯性滚动的时候,借助Handler来事先从RecycledViewPool中取出即将要显⽰的Item,随即扔到mCachedViews中,这样的话,当layout到这个Item时,就能直接拿来⽤⽽不⽤绑定数据了。
为什么我没有说ViewCacheExtension?
因为我发现,这个东西我们开发者根本不能通过常规⼿段来使⽤always getting over you
为什么这么说呢?
castaway
星期五那晚特意⽹上搜了⼀下关于⾃定义ViewCacheExtension的⽂章,但是⼀篇相关的都没有,甚⾄官⽅的库也搜不到,Github上也搜过了,没有!
本来我的想法是这样的:
看到⼤家的回答都没有针对ViewCacheExtension做解释,就想着根据⾃⼰的理解,补充⼀个⾃定义ViewCacheExtension的⽰例:
但是这个抽象类它没有put,只有get,那就只能⾃⼰去获取缓存了,在哪⾥获取呢?
想了⼀下,有两个地⽅⽐较合适(实际上是⼀个地⽅):
1. 重写Adapter的onViewRecycled⽅法;
2. 直接给RecyclerView t⼀个RecyclerListener;
不过我们要做的这个缓存,并不打算缓存普通的Item,因为普通Item,现有的LayoutManager就已经做得很好了,我们应该⽤这个去缓存⼀些Bind⽐较耗时的,或者⼀些内容不会变(可以共享)Item。
emmm,理想很丰满,当我动⼿做时:
java.lang.IllegalArgumentException: Scrapped or attached views may not be recycled. isScrap:fal isAttached:lerview.widget.Recyc        lerview.widget.leViewHolderInternal(RecyclerView.java:6433)
lerview.widget.leView(RecyclerView.java:6369)
lerview.widget.GapWorker.prefetchPositionWithDeadline(GapWorker.java:295)
lerview.widget.GapWorker.flushTaskWithDeadline(GapWorker.java:345)
phillerview.widget.GapWorker.flushTasksWithDeadline(GapWorker.java:361)
lerview.widget.GapWorker.prefetch(GapWorker.java:368)
lerview.widget.GapWorker.run(GapWorker.java:399)
第55届格莱美颁奖典礼
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at flect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
哈哈哈哈哈,就是因为⽤了回收之后的Item,它的Scrap状态没有去掉,当再次被回收时,就报这个错了。
那RecyclerView内部是怎么处理的呢:
public final void bindViewHolder(@NonNull VH holder, int position) {
......
holder.tFlags(ViewHolder.FLAG_BOUND,
ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
| ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
onBindViewHolder(holder, position, UnmodifiedPayloads());
......
}
在回调onBindViewHolder之前,会重置这些状态标记。
这时候可能你会想到:在⾃定义的ViewCacheExtension取出来之前,⼿动把这些状态重置不就⾏了?
没门!
void tFlags(int flags, int mask) {
mFlags = (mFlags & ~mask) | (flags & mask);
}
ViewHolder的tFlags⽅法访问权限是default。
现在你可能会想:既然这样,那就⼲脆不⽤它回收之后的Item,直接⽤Adapter的createViewHolder来事先创建不⾏吗?
还真是不⾏,因为在取出View之后,会对它进⾏验证:
final View view = ViewForPositionAndType(this, position, type);
if (view != null) {
holder = getChildViewHolder(view);
if (holder == null) {
发音字典throw new IllegalArgumentException("getViewForPositionAndType returned"
+ " a view which does not have a ViewHolder"
+ exceptionLabel());
}
}
如果这个View的LayoutParams的mViewHolder实例为空的话,还是会报错的。
那。。那我再⼿动赋值呢?
中文转换英文不好意思,LayoutParams的mViewHolder访问权限也是default。
也就是⽤常规⽅式(⽬前)是⽆法使⽤ViewCacheExtension了,想过⽤反射,但是,缓存这东西的⽬的就是要提⾼效率,为了能使⽤ViewCacheExtension⽽去做降低效率的事情,那就得不偿失了,除⾮你bind view的时间⽐反射所需时间多得多。
以后跟同学们谈论,说到ViewCacheExtension的时候,你就可以⼤声说:“别想了,这东西根本⽤不了的!Google弄出来这个就没想让我们⽤!”
RecyclerView 的观察者模式:
RecyclerView 持有 mObrver 观察者对象,本质为 RecyclerViewDataObrver
音标学习Adaper 持有 mObrvable 被观察对象,本质为 AdapterDataObrvable。
调⽤ RecyclerView.tAdapter(adapter)时将 qisterObrver(obrver)
调⽤ ifydataChange 或者是 notifyitemchanged,实际上是通过obrvable 遍历
obrver 调⽤ onchanged 然后 requestlayout 重新 layout ⼦view

本文发布于:2023-07-15 00:07:27,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/90/177643.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:缓存   滚动   没有   动画   播放   状态   定义   范围
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图