网上有很多关于pos机储存记录已满,Android RecyclerView缓存机制真的很难理解的知识,也有很多人为大家解答关于pos机储存记录已满的问题,今天pos机之家(www.poszjia.com)为大家整理了关于这方面的知识,让我们一起来看下吧!
本文目录一览:
pos机储存记录已满
RecyclerView 的缓存机制,可谓是面试中的常客了。不仅如此,在使用过程中,如果了解这个缓存机制,那么可以更好地利用其特性做开发。
那么,我们将以场景化的方式,讲解 RecyclerView 的缓存机制。常见的两个场景是:
滑动 RecyclerView 下的缓存机制RecyclerView 初次加载过程的缓存机制本文将讲解 滑动 recyclerView 下 的缓存机制
一、缓存层级背景知识:负责回收和复用 ViewHolder 的类是 Recycler,负责缓存的主要就是这个类的几个成员变量。我们贴点源码看看(下面源码的注释(和我写的注释),很重要,要记得认真看哦)
/** * A Recycler is responsible for managing scrapped or detached item views for reuse. * A "scrapped" view is a view that is still attached to its parent RecyclerView but that has been marked for removal or reuse. * * Typical use of a Recycler by a RecyclerView.LayoutManager will be to obtain views * for an adapter's data set representing the data at a given position or item ID. * If the view to be reused is considered "dirty" the adapter will be asked to rebind it. * If not, the view can be quickly reused by the LayoutManager with no further work. * Clean views that have not requested layout may be repositioned by a LayoutManager without remeasurement. */public final class Recycler { final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();// 存放可见范围内的 ViewHolder (但是在 onLayoutChildren 的时候,会将所有 View 都会缓存到这), 从这里复用的 ViewHolder 如果 position 或者 id 对应的上,则不需要重新绑定数据。 ArrayList<ViewHolder> mChangedScrap = null;// 存放可见范围内并且数据发生了变化的 ViewHolder,从这里复用的 ViewHolder 需要重新绑定数据。 final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>(); // 存放 remove 掉的 ViewHolder,从这里复用的 ViewHolder 如果 positIOn 或者 id 对应的上,则不需要重新绑定数据。 private int mRequestedCacheMax = DEFAULT_CACHE_SIZE; // 默认值是 2 int mViewCacheMax = DEFAULT_CACHE_SIZE; // 默认值是 2 RecycledViewPool mRecyclerPool; // 存放 remove 掉,并且重置了数据的 ViewHolder,从这里复用的 ViewHolder 需要重新绑定数据。 // 默认值大小是 5 private ViewCacheExtension mViewCacheExtension; // 自定义的缓存 }
至于到底有几级缓存,我觉得这个问题不大重要。有人说三层,有人说四层。有人说三层,因为觉得自定义那层,不是 RecyclerView 实现的,所以不算;也有人认为 Scrap 并不是真正的缓存,所以不算。
从源码看来,我更同意后者,Scrap 不算一层缓存。因为在源码中,mCachedViews 被称为 first-level。至于为什么 Scrap 不算一层,我的理解是:因为这层的只是 detach 了,并没有 remove,所以这层也没有缓存大小的概念,只要符合规则就会加入进去。
// Search the first-level cachefinal int cacheSize = mCachedViews.size();
类型
变量名
存储说明
备注
Scrap
mAttachedScrap
存放可见范围内的 ViewHolder
从这里复用的 ViewHolder 如果 position 或者 id 对应的上,则不需要重新绑定数据。在 onLayoutChildren 的时候,会将所有 View 都会缓存到这
mChangedScrap
存放可见范围内并且数据发生了变化的 ViewHolder
从这里复用的 ViewHolder 需要重新绑定数据。
Cache
mCachedViews
存放 remove 掉的 ViewHolder
从这里复用的 ViewHolder 如果 position 或者 id 对应的上,则不需要重新绑定数据。
ViewCacheExtension
mViewCacheExtension
自定义缓存
RecycledViewPool
mRecyclerPool
存放 remove 掉,并且重置了数据的 ViewHolder
从这里复用的 ViewHolder 需要重新绑定数据。
二、场景分析:滑动中的 RecyclerView 缓存机制通过 Android Studio 的 Profiles 工具,我们可以看到调用流程
入口是 ouTouchEvent
通过表格的方式,简要说明上图的流程都在做什么?
方法名
隶属的类
作用描述
onTouchEvent()
RecyclerView
处理点击事件,在 MOVE 事件中在一定条件下,拦截事件后,做事件处理
scrollByInternal()
RecyclerView
主要是调用 scrollStep()
scrollStep()
RecyclerView
通过 dx 和 dy 的值判断是调用scrollHorizontallyBy()还是 scrollVerticallyBy()
scrollHorizontallyBy()/scrollVerticallyBy()
LayoutManager
主要是调用 scrollBy()
scrollBy()
LayoutManager
通过调用 fill() 添加滑进来的View 和回收滑出去的 View
offsetChildrenVertical()/offsetChildrenHorizontal()
RecyclerView
做偏移操作
通过上述表格,我们知道了。最重要的东西那就是 scrollBy 中调用了 fill 的方法了。那我们看看 fill 在做什么吧?滑出去的 View 最后去哪里了呢?滑进来的 View 是怎么来的?(带着这个问题,我们一起来读源码!一定要带着),源码只留下了核心部分
int fill(RecyclerView.Recycler recycler, LayoutState layoutState, RecyclerView.State state, boolean stopOnFocusable) { // max offset we should set is mFastScroll + available final int start = layoutState.mAvailable; //首选该语句块的判断,判断当前状态是否为滚动状态,如果是的话,则触发 recycleByLayoutState 方法 if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) { // TODO ugly bug fix. should not happen if (layoutState.mAvailable < 0) { layoutState.mScrollingOffset += layoutState.mAvailable; } // 分析1----回收 recycleByLayoutState(recycler, layoutState); } while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) { //分析2----复用 layoutChunk(recycler, state, layoutState, layoutChunkResult); }}
// 分析1----回收 // 通过一步步追踪,我们发现最后调用的是 removeAndRecycleViewAt() public void removeAndRecycleViewAt(int index, @NonNull Recycler recycler) { final View view = getChildAt(index); //分析1-1 removeViewAt(index); //分析1-2 recycler.recycleView(view);}// 分析1-1// 从 RecyclerView 移除一个 View public void removeViewAt(int index) { final View child = getChildAt(index); if (child != null) { mChildHelper.removeViewAt(index); }}//分析1-2 // recycler.recycleView(view) 最终调用的是 recycleViewHolderInternal(holder) 进行回收 VH (ViewHolder)void recycleViewHolderInternal(ViewHolder holder) { if (forceRecycle || holder.isRecyclable()) { //判断是否满足放进 mCachedViews if (mViewCacheMax > 0 && !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID| ViewHolder.FLAG_REMOVED| ViewHolder.FLAG_UPDATE| ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)){ // 判断 mCachedViews 是否已满 if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) { // 如果满了就将下标为0(即最早加入的)移除,同时将其加入到 RecyclerPool 中 recycleCachedViewAt(0); cachedViewSize--; } mCachedViews.add(targetCacheIndex, holder); cached = true; } //如果没有满足上面的条件,则直接存进 RecyclerPool 中 if (!cached) { addViewHolderToRecycledViewPool(holder, true); recycled = true; } }}
//分析2void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, LayoutState layoutState, LayoutChunkResult result) { //分析2-1 View view = layoutState.next(recycler); if (layoutState.mScrapList == null) { if (mShouldReverseLayout == (layoutState.mLayoutDirection == LayoutState.LAYOUT_START)) { //添加到 RecyclerView 上 addView(view); } else { addView(view, 0); } }}//分析2-1//layoutState.next(recycler) 最后调用的是 tryGetViewHolderForPositionByDeadline() 这个方法正是 复用 核心的方法ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs) { // 0) If there is a changed scrap, try to find from there // 例如:我们调用 notifyItemChanged 方法时 if (mState.isPreLayout()) { // 如果是 changed 的 ViewHolder 那么就先从 mChangedScrap 中找 holder = getChangedScrapViewForPosition(position); fromScrapOrHiddenOrCache = holder != null; } // 1) Find by position from scrap/hidden list/cache if (holder == null) { //如果在上面没有找到(holder == null),那就尝试从通过 pos 在 mAttachedScrap/ mHiddenViews / mCachedViews 中获取 holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun); } if (holder == null) { // 2) Find from scrap/cache via stable ids, if exists if (mAdapter.hasStableIds()) { //如果在上面没有找到(holder == null),那就尝试从通过 id 在 mAttachedScrap/ mCachedViews 中获取 holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition), } if (holder == null && mViewCacheExtension != null) { //这里是通过自定义缓存中获取,忽略 } //如果在上面都没有找到(holder == null),那就尝试在 RecycledViewPool 中获取 if (holder == null) { // fallback to pool holder = getRecycledViewPool().getRecycledView(type); if (holder != null) { //这里拿的是,要清空数据的 holder.resetInternal(); } } //如果在 Scrap / Hidden / Cache / RecycledViewPool 都没有找到,那就只能创建一个了。 if (holder == null) { holder = mAdapter.createViewHolder(RecyclerView.this, type); } } return holder;}复制代码总结
做一个总结,在分析源码前,我们提出了三个问题,那看看答案是什么吧
Q:那我们看看 fill 在做什么吧?A:其实就是分析1(回收 ViewHolder ) + 分析 2 ( 复用 ViewHolder )
Q:滑出去的 View 最后去哪里了呢?A:先尝试回收到 mCachedViews 中,未成功,则回收到 RecycledViewPool 中。
Q:滑进来的 View 是怎么来的?A:如果是 isPreLayout 则先从 mChangedScrap 中尝试获取。未获取到,再从 mAttachedScrap / mHiddenViews / mCachedViews (通过 position ) 中尝试获取未获取到,再从 mAttachedScrap / mCachedViews (通过 id)中尝试获取未获取到,再从 自定义缓存中尝试获取未获取到,再从 RecycledViewPool 中尝试获取未获取到,创建一个新的 ViewHolder最后
这里也分享一些珍藏资源,从面试简历模板到大厂面经汇总,从大厂内部技术资料到互联网高薪必读书单,以及Android面试核心知识点(844页)和Android面试题合集2022年最新版(354页)等等,这些资料整理给大家,希望踩过的坑不要再踩,遭遇的技术瓶颈一次性消灭。
如果需要的话,可以顺手帮我点赞评论一下,直接私信我【笔记】免费领取!
部分内容展示如下01.Android必备底层技术:
Java序列化:Serializable原理、Parcelable接口原理、Json、XML注解、泛型与反射:自定义注解、注解的使用、泛型擦除机制、泛型边界、Java方法与Arm指令、Method反射源码、invoke方法执行原理虚拟机:JVM垃圾回收器机制、JVM内存分配策略、Android虚拟机与JVM底层区别、虚拟机底层Odex本地指令缓存机制、虚拟机如何分别加载class与object、虚拟机类加载模型并发:Java线程本质讲解、线程原理、线程通信、UnSafe类、线程池编译时技术:OOP面向切面之AspectJ、字节码手术刀JavaSSit实战、字节码插桩技术(ASM)实战动态代理:动态代理实现原理、动态代理在虚拟机中运行时动态拼接Class字节码分析、ProxyGenerator生成字节码流程高级数据结构与算法:HashMap源码、ArrayList源码、排序算法Java IO:Java IO体系、IO文件操作02.Framework:
Binder:Linux内存基础、Binder四层源码分析、Binder机制、Binder进程通信原理Handler:Loop消息泵机制、Message解析Zygote:init进程与Zygote进程、Zygote启动流程、Socket通信模式、APP启动过程AMS:ActivityThread源码分析、AMS与ActivityThread通信原理、Activity启动机制PMS:PMS源码、APK安装过程分析、PMS对安装包的解析原理WMS:PhoneWindow实例化流程、DecorView创建过程、ViewRootImpl渲染机制03.Android常用组件:
Activty:Activity管理栈与Activity的启动模式、Activity生命周期源码分析Fragment:Fragment生命周期深入详解、Fragment事务管理机制详解、性能优化相关方案Service:Service启动模式分析、Service管理与通信方案、Service生命周期底层详解04.高级UI:
UI绘制原理:setContentView()方法下到底做了什么、AppCompatActivity与Activity的区别、UI测量、布局、绘制的底层执行流程插件换肤:LayoutInflater加载布局分析、Android资源的加载机制、Resource与AssetManager事件分发机制原理:事件执行U形链与L形链、事件拦截原理属性动画:VSYNC刷新机制、ObjectAnimator与ValueAnimator源码讲解、Android属性动画:插值器与估值器RecycleView:布局管理器LayoutManager详解、回收池设计思想、适配器模式原理高阶贝塞尔曲线05.Jetpack:
Lifecycle:Lifecycle源码、Lifecycle高阶应用ViewModel:ViewModel源码、ViewModel应用技巧LiveData:LiveData源码Navigation:Navigation源码Room:Room源码、Room+LiveData监听数据库数据变更刷新页面原理WorkManager内核Pagging原理DataBinding:单向绑定、双向绑定、如何与RecyclerView的配合使用、底层原理06.性能优化:
启动优化:系统启动原理、Trace工具分析启动卡顿、类重排机制、资源文件重排机制内存优化UI渲染优化:UI层级规范及对UI加载的影响、UI卡顿原因及修复、UI绘制、布局、测量原因以及处理方案卡顿优化:造成卡顿的原因分析、内存抖动与GC回收、回收算法耗电优化崩溃优化:项目崩溃异常捕获、优雅的异常处理方案、如何避免异常弹框安全优化:APP加固实现(防反编译,dex加固)、https防抓包机制(数据传输加载,客户端服务器端双向加密校验)网络优化:serializable原理、parcelable接口原理、http与https原理详解、protbuffer网络IO详解、gzip压缩方案大图加载优化:Glide巨图加载机制原理分析、大图多级缓存实现方案多线程并发优化储存优化:Android文件系统-sdcard与内存存储、Shared Preference原理、MMAP内存映射安装包优化:shrinkResources去除无用资源、合理设置多语言、webp实现图片瘦身、合理配置armable-v7的so库、Lint检查工具实践如果需要的话,可以顺手帮我点赞评论一下,直接私信我【笔记】免费领取!
07.音视频:
C/C++:数据类型、数组、内存布局、指针、函数、预处理器、结构体、共用体、容器、类型转换、异常、文件流操作、线程H.265/H.265:音视频格式封装原理、编码原理、视频流H264的组装原理切片NAL单元、视频流H264码流分析、切片与宏快,运动矢量、信源编码器、高频滤波、帧间拆分与帧内预测、CTU,PU TU编码结构、DSP芯片解码流程、MediaPlayer与DSP芯片交互机制、投屏架构、MediaProjection与MeidiaCodec交互机制、H265码流交换MediaCodec:dsp芯片、编解码器的生命周期、解码器中输入队列与解析队列设计思想、MediaCodec中平缓解码解析、MediaExtractor 多路复用、MediaMuxer合成器、MediaFormat格式音视频剪辑:视频剪辑、音频剪辑、音频合成、音谱显示、视频倒放音视频直播:硬编码、软编码、native实现rtmp推流、摄像头预览帧编码NV21转YUV、视频画面封装拼接Packet包、音频流数据拼接Packet包、RtmpDump实时同步发送音视频数据、MediaProjection、Medicodec编码H264码流、rtmp推流OpenGL与音视频解码:OpenGL绘制流程、矩阵、Opencv详解、人脸识别效果实现OpenGL特效:CPU与GPU运行机制详解、世界坐标,布局坐标,与FBO坐标系、图像镜像与旋转处理、人脸定位与关键点定位、大眼效果、贴纸效果、美颜效果FFmpeg万能播放器:FFmpeg结构体、声音播放原理、Surface的渲染、像素绘制原理与对齐机制、音视频同步原理、视频播放器整体架构Webrtc音视频通话:WebRtc服务端环境搭建与Webrtc编译、1v1视频通话实现方案、群聊视频通话实现思路、多对多视频会议实现、1V1音视频通话实现08.开源框架原理:
OkhttpRetrofitRxJavaGlideHiltDagger2EventBus组件化、插件化、热修复等09.Gradle:
Groovy语法Gradle Android插件配置Gradle实践等10.kotlin:
Kotlin语法扩展使用进阶使用实践等11.Flutter:
Dart语法UI进阶使用优化实践等12.鸿蒙:
Ability组件分布式任务事件总线鸿蒙线程UI自定义控件等如果需要的话,可以顺手帮我点赞评论一下,直接私信我【笔记】免费领取!
Android路漫漫,共勉!
以上就是关于pos机储存记录已满,Android RecyclerView缓存机制真的很难理解的知识,后面我们会继续为大家整理关于pos机储存记录已满的知识,希望能够帮助到大家!
