删除View缓存使用图解
在垂直布局的RecyclerView中展示了Item0~Item4这5个TextView,我们现在把Item2删掉。数据更新后,我们调用Adapter#notifyItemRemoved(2)
来提交更新。
dispatchLayoutStep1阶段:
- 调用detachViewFromParent把Item0~Item4这五个View从RecyclerView中detache掉,然后放入把ViewHolder放入mAttachedScrap进行缓存。ViewHodler都会被标记为FLAG_TMP_DETACHED,并且Item2会被标记为FLAG_REMOVED;
- 然后layout进行填充,把Item0~Item4从mAttachedScrap中取出,然后调用attachViewToParent重新加入到RecyclerView中。
- 调用onCreateViewHolder创建一个新ViewHolder(Item5),执行onBindViewHolder,并addView到RecyclerView中。
dispatchLayoutStep2阶段:
- 调用detachViewFromParent把Item0~Item5这六个View从RecyclerView中detache掉,然后把ViewHolder放入mAttachedScrap进行缓存。ViewHolder都会被标记为FLAG_TMP_DETACHED,并且Item2会被标记为FLAG_REMOVED;
- 除了Item2的ViewHolder之外,把其他ViewHolder从mAttachedScrap缓存中取出,然后调用attachViewToParent重新加入到RecyclerView中。
dispatchLayoutStep3阶段:
- 从mAttachedScrap取出Item2,然后把Item2 View添加到mHiddenViews中;
- 在Item2 ItemAnimator的onAnimationFinished事件中把Item2从mAttachedScrap中移除,添加到RecycledViewPool中。
修改View缓存使用图解
RecyclerView中包含Item0~Item4共五个TextView,我们把Item2的文案更新为”New 2”。通过调用Adapter#notifyItemChanged(2)
来通知RecyclerView进行更新。
插入View缓存使用图解
预加载缓存 mCachedViews
预加载的view会放在这里,这个数组最大默认是2。首次绘制出来,如果没有滑动屏幕,mCachedViews为空,当滑动UI后会将预加载的一个,以及划出的加入缓存中。
如果是向下滑动,则屏幕下面的元素会缓存两个,比如上图右边向下把H滑出屏幕,而C会露出,则mCachedViews中保存的是[B, H, I]。
滑动时预加载流程
androidx.recyclerview.widget.RecyclerView#onTouchEvent
接收到MotionEvent.ACTION_MOVE
事件时,如果判断滑动超过一定的距离时,就任务用户自滑动操作。然后就会调用GapWorker#postFromTraversal
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void postFromTraversal(RecyclerView recyclerView, int prefetchDx, int prefetchDy) { if (recyclerView.isAttachedToWindow()) { if (RecyclerView.DEBUG && !mRecyclerViews.contains(recyclerView)) { throw new IllegalStateException("attempting to post unregistered view!"); } if (mPostTimeNs == 0) { mPostTimeNs = recyclerView.getNanoTime(); recyclerView.post(this); } } recyclerView.mPrefetchRegistry.setPrefetchVector(prefetchDx, prefetchDy); }
|
mAttachedScrap
先看下源码执行流程:
从上面时序图可以看出,最终调用到了父类View#requestLayout()
,然后调用mParent.requestLayout();
,沿着view层级一直调用到android.view.ViewRootImpl#requestLayout()
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); } } void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } } final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } } final TraversalRunnable mTraversalRunnable = new TraversalRunnable(); void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) { Debug.startMethodTracing("ViewAncestor"); }
performTraversals();
if (mProfile) { Debug.stopMethodTracing(); mProfile = false; } } }
|
从上面可以看出,requestLayout不会立马执行,而是等待下一个vsync信号到来再执行,最终执行android.view.ViewRootImpl#performTraversals
,这个函数很长有800多行,这里就不贴代码了,这里面就会View的绘制的measure、layout、draw三大流程。分别对应:ViewRootImpl#measureHierarchy
、ViewRootImpl#performLayout
、ViewRootImpl#performDraw
。
从缓存获取ViewHolder
首次创建ViewHolder,以及从缓存中获取都是通过androidx.recyclerview.widget.RecyclerView.Recycler#getViewForPosition(int)
,这个方法会优先使用缓存中ViewHolder,如果找不到,就会调用createViewHolder和bindViewHolder创建一个新的,并返回ViewHodler的itemView:
ItemAnimator