網(wǎng)上有很多關(guān)于pos機(jī)儲存記錄已滿,Android RecyclerView緩存機(jī)制真的很難理解的知識,也有很多人為大家解答關(guān)于pos機(jī)儲存記錄已滿的問題,今天pos機(jī)之家(www.rcqwhg.com)為大家整理了關(guān)于這方面的知識,讓我們一起來看下吧!
本文目錄一覽:
pos機(jī)儲存記錄已滿
RecyclerView 的緩存機(jī)制,可謂是面試中的常客了。不僅如此,在使用過程中,如果了解這個(gè)緩存機(jī)制,那么可以更好地利用其特性做開發(fā)。
那么,我們將以場景化的方式,講解 RecyclerView 的緩存機(jī)制。常見的兩個(gè)場景是:
滑動 RecyclerView 下的緩存機(jī)制RecyclerView 初次加載過程的緩存機(jī)制本文將講解 滑動 recyclerView 下 的緩存機(jī)制
一、緩存層級背景知識:負(fù)責(zé)回收和復(fù)用 ViewHolder 的類是 Recycler,負(fù)責(zé)緩存的主要就是這個(gè)類的幾個(gè)成員變量。我們貼點(diǎn)源碼看看(下面源碼的注釋(和我寫的注釋),很重要,要記得認(rèn)真看哦)
/** * 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<>();// 存放可見范圍內(nèi)的 ViewHolder (但是在 onLayoutChildren 的時(shí)候,會將所有 View 都會緩存到這), 從這里復(fù)用的 ViewHolder 如果 position 或者 id 對應(yīng)的上,則不需要重新綁定數(shù)據(jù)。 ArrayList<ViewHolder> mChangedScrap = null;// 存放可見范圍內(nèi)并且數(shù)據(jù)發(fā)生了變化的 ViewHolder,從這里復(fù)用的 ViewHolder 需要重新綁定數(shù)據(jù)。 final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>(); // 存放 remove 掉的 ViewHolder,從這里復(fù)用的 ViewHolder 如果 positIOn 或者 id 對應(yīng)的上,則不需要重新綁定數(shù)據(jù)。 private int mRequestedCacheMax = DEFAULT_CACHE_SIZE; // 默認(rèn)值是 2 int mViewCacheMax = DEFAULT_CACHE_SIZE; // 默認(rèn)值是 2 RecycledViewPool mRecyclerPool; // 存放 remove 掉,并且重置了數(shù)據(jù)的 ViewHolder,從這里復(fù)用的 ViewHolder 需要重新綁定數(shù)據(jù)。 // 默認(rèn)值大小是 5 private ViewCacheExtension mViewCacheExtension; // 自定義的緩存 }
至于到底有幾級緩存,我覺得這個(gè)問題不大重要。有人說三層,有人說四層。有人說三層,因?yàn)橛X得自定義那層,不是 RecyclerView 實(shí)現(xiàn)的,所以不算;也有人認(rèn)為 Scrap 并不是真正的緩存,所以不算。
從源碼看來,我更同意后者,Scrap 不算一層緩存。因?yàn)樵谠创a中,mCachedViews 被稱為 first-level。至于為什么 Scrap 不算一層,我的理解是:因?yàn)檫@層的只是 detach 了,并沒有 remove,所以這層也沒有緩存大小的概念,只要符合規(guī)則就會加入進(jìn)去。
// Search the first-level cachefinal int cacheSize = mCachedViews.size();
類型
變量名
存儲說明
備注
Scrap
mAttachedScrap
存放可見范圍內(nèi)的 ViewHolder
從這里復(fù)用的 ViewHolder 如果 position 或者 id 對應(yīng)的上,則不需要重新綁定數(shù)據(jù)。在 onLayoutChildren 的時(shí)候,會將所有 View 都會緩存到這
mChangedScrap
存放可見范圍內(nèi)并且數(shù)據(jù)發(fā)生了變化的 ViewHolder
從這里復(fù)用的 ViewHolder 需要重新綁定數(shù)據(jù)。
Cache
mCachedViews
存放 remove 掉的 ViewHolder
從這里復(fù)用的 ViewHolder 如果 position 或者 id 對應(yīng)的上,則不需要重新綁定數(shù)據(jù)。
ViewCacheExtension
mViewCacheExtension
自定義緩存
RecycledViewPool
mRecyclerPool
存放 remove 掉,并且重置了數(shù)據(jù)的 ViewHolder
從這里復(fù)用的 ViewHolder 需要重新綁定數(shù)據(jù)。
二、場景分析:滑動中的 RecyclerView 緩存機(jī)制通過 Android Studio 的 Profiles 工具,我們可以看到調(diào)用流程
入口是 ouTouchEvent
通過表格的方式,簡要說明上圖的流程都在做什么?
方法名
隸屬的類
作用描述
onTouchEvent()
RecyclerView
處理點(diǎn)擊事件,在 MOVE 事件中在一定條件下,攔截事件后,做事件處理
scrollByInternal()
RecyclerView
主要是調(diào)用 scrollStep()
scrollStep()
RecyclerView
通過 dx 和 dy 的值判斷是調(diào)用scrollHorizontallyBy()還是 scrollVerticallyBy()
scrollHorizontallyBy()/scrollVerticallyBy()
LayoutManager
主要是調(diào)用 scrollBy()
scrollBy()
LayoutManager
通過調(diào)用 fill() 添加滑進(jìn)來的View 和回收滑出去的 View
offsetChildrenVertical()/offsetChildrenHorizontal()
RecyclerView
做偏移操作
通過上述表格,我們知道了。最重要的東西那就是 scrollBy 中調(diào)用了 fill 的方法了。那我們看看 fill 在做什么吧?滑出去的 View 最后去哪里了呢?滑進(jìn)來的 View 是怎么來的?(帶著這個(gè)問題,我們一起來讀源碼!一定要帶著),源碼只留下了核心部分
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; //首選該語句塊的判斷,判斷當(dāng)前狀態(tài)是否為滾動狀態(tài),如果是的話,則觸發(fā) 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----復(fù)用 layoutChunk(recycler, state, layoutState, layoutChunkResult); }}
// 分析1----回收 // 通過一步步追蹤,我們發(fā)現(xiàn)最后調(diào)用的是 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 移除一個(gè) View public void removeViewAt(int index) { final View child = getChildAt(index); if (child != null) { mChildHelper.removeViewAt(index); }}//分析1-2 // recycler.recycleView(view) 最終調(diào)用的是 recycleViewHolderInternal(holder) 進(jìn)行回收 VH (ViewHolder)void recycleViewHolderInternal(ViewHolder holder) { if (forceRecycle || holder.isRecyclable()) { //判斷是否滿足放進(jìn) 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) { // 如果滿了就將下標(biāo)為0(即最早加入的)移除,同時(shí)將其加入到 RecyclerPool 中 recycleCachedViewAt(0); cachedViewSize--; } mCachedViews.add(targetCacheIndex, holder); cached = true; } //如果沒有滿足上面的條件,則直接存進(jìn) 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) 最后調(diào)用的是 tryGetViewHolderForPositionByDeadline() 這個(gè)方法正是 復(fù)用 核心的方法ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs) { // 0) If there is a changed scrap, try to find from there // 例如:我們調(diào)用 notifyItemChanged 方法時(shí) 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) { //這里拿的是,要清空數(shù)據(jù)的 holder.resetInternal(); } } //如果在 Scrap / Hidden / Cache / RecycledViewPool 都沒有找到,那就只能創(chuàng)建一個(gè)了。 if (holder == null) { holder = mAdapter.createViewHolder(RecyclerView.this, type); } } return holder;}復(fù)制代碼總結(jié)
做一個(gè)總結(jié),在分析源碼前,我們提出了三個(gè)問題,那看看答案是什么吧
Q:那我們看看 fill 在做什么吧?A:其實(shí)就是分析1(回收 ViewHolder ) + 分析 2 ( 復(fù)用 ViewHolder )
Q:滑出去的 View 最后去哪里了呢?A:先嘗試回收到 mCachedViews 中,未成功,則回收到 RecycledViewPool 中。
Q:滑進(jìn)來的 View 是怎么來的?A:如果是 isPreLayout 則先從 mChangedScrap 中嘗試獲取。未獲取到,再從 mAttachedScrap / mHiddenViews / mCachedViews (通過 position ) 中嘗試獲取未獲取到,再從 mAttachedScrap / mCachedViews (通過 id)中嘗試獲取未獲取到,再從 自定義緩存中嘗試獲取未獲取到,再從 RecycledViewPool 中嘗試獲取未獲取到,創(chuàng)建一個(gè)新的 ViewHolder最后
這里也分享一些珍藏資源,從面試簡歷模板到大廠面經(jīng)匯總,從大廠內(nèi)部技術(shù)資料到互聯(lián)網(wǎng)高薪必讀書單,以及Android面試核心知識點(diǎn)(844頁)和Android面試題合集2022年最新版(354頁)等等,這些資料整理給大家,希望踩過的坑不要再踩,遭遇的技術(shù)瓶頸一次性消滅。
如果需要的話,可以順手幫我點(diǎn)贊評論一下,直接私信我【筆記】免費(fèi)領(lǐng)??!
部分內(nèi)容展示如下01.Android必備底層技術(shù):
Java序列化:Serializable原理、Parcelable接口原理、Json、XML注解、泛型與反射:自定義注解、注解的使用、泛型擦除機(jī)制、泛型邊界、Java方法與Arm指令、Method反射源碼、invoke方法執(zhí)行原理虛擬機(jī):JVM垃圾回收器機(jī)制、JVM內(nèi)存分配策略、Android虛擬機(jī)與JVM底層區(qū)別、虛擬機(jī)底層Odex本地指令緩存機(jī)制、虛擬機(jī)如何分別加載class與object、虛擬機(jī)類加載模型并發(fā):Java線程本質(zhì)講解、線程原理、線程通信、UnSafe類、線程池編譯時(shí)技術(shù):OOP面向切面之AspectJ、字節(jié)碼手術(shù)刀JavaSSit實(shí)戰(zhàn)、字節(jié)碼插樁技術(shù)(ASM)實(shí)戰(zhàn)動態(tài)代理:動態(tài)代理實(shí)現(xiàn)原理、動態(tài)代理在虛擬機(jī)中運(yùn)行時(shí)動態(tài)拼接Class字節(jié)碼分析、ProxyGenerator生成字節(jié)碼流程高級數(shù)據(jù)結(jié)構(gòu)與算法:HashMap源碼、ArrayList源碼、排序算法Java IO:Java IO體系、IO文件操作02.Framework:
Binder:Linux內(nèi)存基礎(chǔ)、Binder四層源碼分析、Binder機(jī)制、Binder進(jìn)程通信原理Handler:Loop消息泵機(jī)制、Message解析Zygote:init進(jìn)程與Zygote進(jìn)程、Zygote啟動流程、Socket通信模式、APP啟動過程AMS:ActivityThread源碼分析、AMS與ActivityThread通信原理、Activity啟動機(jī)制PMS:PMS源碼、APK安裝過程分析、PMS對安裝包的解析原理WMS:PhoneWindow實(shí)例化流程、DecorView創(chuàng)建過程、ViewRootImpl渲染機(jī)制03.Android常用組件:
Activty:Activity管理?xiàng)EcActivity的啟動模式、Activity生命周期源碼分析Fragment:Fragment生命周期深入詳解、Fragment事務(wù)管理機(jī)制詳解、性能優(yōu)化相關(guān)方案Service:Service啟動模式分析、Service管理與通信方案、Service生命周期底層詳解04.高級UI:
UI繪制原理:setContentView()方法下到底做了什么、AppCompatActivity與Activity的區(qū)別、UI測量、布局、繪制的底層執(zhí)行流程插件換膚:LayoutInflater加載布局分析、Android資源的加載機(jī)制、Resource與AssetManager事件分發(fā)機(jī)制原理:事件執(zhí)行U形鏈與L形鏈、事件攔截原理屬性動畫:VSYNC刷新機(jī)制、ObjectAnimator與ValueAnimator源碼講解、Android屬性動畫:插值器與估值器RecycleView:布局管理器LayoutManager詳解、回收池設(shè)計(jì)思想、適配器模式原理高階貝塞爾曲線05.Jetpack:
Lifecycle:Lifecycle源碼、Lifecycle高階應(yīng)用ViewModel:ViewModel源碼、ViewModel應(yīng)用技巧LiveData:LiveData源碼Navigation:Navigation源碼Room:Room源碼、Room+LiveData監(jiān)聽數(shù)據(jù)庫數(shù)據(jù)變更刷新頁面原理WorkManager內(nèi)核Pagging原理DataBinding:單向綁定、雙向綁定、如何與RecyclerView的配合使用、底層原理06.性能優(yōu)化:
啟動優(yōu)化:系統(tǒng)啟動原理、Trace工具分析啟動卡頓、類重排機(jī)制、資源文件重排機(jī)制內(nèi)存優(yōu)化UI渲染優(yōu)化:UI層級規(guī)范及對UI加載的影響、UI卡頓原因及修復(fù)、UI繪制、布局、測量原因以及處理方案卡頓優(yōu)化:造成卡頓的原因分析、內(nèi)存抖動與GC回收、回收算法耗電優(yōu)化崩潰優(yōu)化:項(xiàng)目崩潰異常捕獲、優(yōu)雅的異常處理方案、如何避免異常彈框安全優(yōu)化:APP加固實(shí)現(xiàn)(防反編譯,dex加固)、https防抓包機(jī)制(數(shù)據(jù)傳輸加載,客戶端服務(wù)器端雙向加密校驗(yàn))網(wǎng)絡(luò)優(yōu)化:serializable原理、parcelable接口原理、http與https原理詳解、protbuffer網(wǎng)絡(luò)IO詳解、gzip壓縮方案大圖加載優(yōu)化:Glide巨圖加載機(jī)制原理分析、大圖多級緩存實(shí)現(xiàn)方案多線程并發(fā)優(yōu)化儲存優(yōu)化:Android文件系統(tǒng)-sdcard與內(nèi)存存儲、Shared Preference原理、MMAP內(nèi)存映射安裝包優(yōu)化:shrinkResources去除無用資源、合理設(shè)置多語言、webp實(shí)現(xiàn)圖片瘦身、合理配置armable-v7的so庫、Lint檢查工具實(shí)踐如果需要的話,可以順手幫我點(diǎn)贊評論一下,直接私信我【筆記】免費(fèi)領(lǐng)?。?/strong>
07.音視頻:
C/C++:數(shù)據(jù)類型、數(shù)組、內(nèi)存布局、指針、函數(shù)、預(yù)處理器、結(jié)構(gòu)體、共用體、容器、類型轉(zhuǎn)換、異常、文件流操作、線程H.265/H.265:音視頻格式封裝原理、編碼原理、視頻流H264的組裝原理切片NAL單元、視頻流H264碼流分析、切片與宏快,運(yùn)動矢量、信源編碼器、高頻濾波、幀間拆分與幀內(nèi)預(yù)測、CTU,PU TU編碼結(jié)構(gòu)、DSP芯片解碼流程、MediaPlayer與DSP芯片交互機(jī)制、投屏架構(gòu)、MediaProjection與MeidiaCodec交互機(jī)制、H265碼流交換MediaCodec:dsp芯片、編解碼器的生命周期、解碼器中輸入隊(duì)列與解析隊(duì)列設(shè)計(jì)思想、MediaCodec中平緩解碼解析、MediaExtractor 多路復(fù)用、MediaMuxer合成器、MediaFormat格式音視頻剪輯:視頻剪輯、音頻剪輯、音頻合成、音譜顯示、視頻倒放音視頻直播:硬編碼、軟編碼、native實(shí)現(xiàn)rtmp推流、攝像頭預(yù)覽幀編碼NV21轉(zhuǎn)YUV、視頻畫面封裝拼接Packet包、音頻流數(shù)據(jù)拼接Packet包、RtmpDump實(shí)時(shí)同步發(fā)送音視頻數(shù)據(jù)、MediaProjection、Medicodec編碼H264碼流、rtmp推流OpenGL與音視頻解碼:OpenGL繪制流程、矩陣、Opencv詳解、人臉識別效果實(shí)現(xiàn)OpenGL特效:CPU與GPU運(yùn)行機(jī)制詳解、世界坐標(biāo),布局坐標(biāo),與FBO坐標(biāo)系、圖像鏡像與旋轉(zhuǎn)處理、人臉定位與關(guān)鍵點(diǎn)定位、大眼效果、貼紙效果、美顏效果FFmpeg萬能播放器:FFmpeg結(jié)構(gòu)體、聲音播放原理、Surface的渲染、像素繪制原理與對齊機(jī)制、音視頻同步原理、視頻播放器整體架構(gòu)Webrtc音視頻通話:WebRtc服務(wù)端環(huán)境搭建與Webrtc編譯、1v1視頻通話實(shí)現(xiàn)方案、群聊視頻通話實(shí)現(xiàn)思路、多對多視頻會議實(shí)現(xiàn)、1V1音視頻通話實(shí)現(xiàn)08.開源框架原理:
OkhttpRetrofitRxJavaGlideHiltDagger2EventBus組件化、插件化、熱修復(fù)等09.Gradle:
Groovy語法Gradle Android插件配置Gradle實(shí)踐等10.kotlin:
Kotlin語法擴(kuò)展使用進(jìn)階使用實(shí)踐等11.Flutter:
Dart語法UI進(jìn)階使用優(yōu)化實(shí)踐等12.鴻蒙:
Ability組件分布式任務(wù)事件總線鴻蒙線程UI自定義控件等如果需要的話,可以順手幫我點(diǎn)贊評論一下,直接私信我【筆記】免費(fèi)領(lǐng)取!
Android路漫漫,共勉!
以上就是關(guān)于pos機(jī)儲存記錄已滿,Android RecyclerView緩存機(jī)制真的很難理解的知識,后面我們會繼續(xù)為大家整理關(guān)于pos機(jī)儲存記錄已滿的知識,希望能夠幫助到大家!