持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情
目录
- Android 源码浅析:RecyclerView 源码浅析(1)—— 回收、复用、预加载机制
- Android 源码浅析:RecyclerView 源码浅析(2)—— 测量、布局、绘制、预布局
- Android 源码浅析:RecyclerView 源码浅析(3)—— LayoutManager
- Android 源码浅析:RecyclerView 源码浅析(4)—— ItemDecoration
- Android 源码浅析:RecyclerView 源码浅析(5)—— ItemAnimator
- Android 源码浅析:RecyclerView 源码浅析(6)—— Adapter
前言
本系列博客将分享 RecyclerView 源码相关知识,RecyclerView 是日常开发非常常用的 View,同时官方对其的设计也非常优秀值得我们去学习。
本博客从老生常谈的回收复用机制部分源码入手,从源码角度来理解回收复用、预加载机制。
源码版本
implementation 'androidx.recyclerview:recyclerview:1.2.1'
概述
切入点
我们知道当 RecyclerView 上下滑动时那些移出屏幕的 ViewHolder 会进行回收,新进入屏幕的 ViewHolder 会进行复用(如果有缓存),那么对于回收复用的源码分析就要从触摸方法中开始找切入点,先看一下 onTouchEvent 方法中处理滑动的调用流程:
RecyclerView.java
public boolean onTouchEvent(MotionEvent e) {
// ...
// mLayout 即为 LayoutManager 可以看出不设置 LayoutManager 是无法触发滑动的
if (mLayout == null) {
return false;
}
// ...
switch (action) {
case MotionEvent.ACTION_DOWN: {}
case MotionEvent.ACTION_MOVE: {
// ...
// 根据 Down 事件中保存的信息计算出滑动距离
int dx = mLastTouchX - x;
int dy = mLastTouchY - y;
// 滑动前先进行方向判断
if (mScrollState != SCROLL_STATE_DRAGGING) {
boolean startScroll = false;
// ...
if (canScrollVertically) { // 是否可以垂直滑动
if (dy > 0) {
dy = Math.max(0, dy - mTouchSlop);
} else {
dy = Math.min(0, dy + mTouchSlop);
}
if (dy != 0) {
startScroll = true;
}
}
if (startScroll) { // 标记开始滑动
setScrollState(SCROLL_STATE_DRAGGING);
}
}
if (mScrollState == SCROLL_STATE_DRAGGING) {
// ...
// scrollByInternal 是处理滑动的方法 !!!
if (scrollByInternal(
canScrollHorizontally ? dx : 0,
canScrollVertically ? dy : 0,
e, TYPE_TOUCH)) {
getParent().requestDisallowInterceptTouchEvent(true);
}
// ...
}
}
// ...
}
// scrollByInternal 源码部分
boolean scrollByInternal(int x, int y, MotionEvent ev, int type) {
// ...
if (mAdapter != null) {
mReusableIntPair[0] = 0;
mReusableIntPair[1] = 0;
// scrollStep 方法中计算出了滑动消耗的距离,并且赋值给了 mReusableIntPair
scrollStep(x, y, mReusableIntPair);
consumedX = mReusableIntPair[0];
consumedY = mReusableIntPair[1];
unconsumedX = x - consumedX;
unconsumedY = y - consumedY;
}
// ...
}
// scrollStep 源码部分
void scrollStep(int dx, int dy, @Nullable int[] consumed) {
// ...
int consumedX = 0;
int consumedY = 0;
// 不管是水平还是垂直滑动 最终都调用到了 LayoutManager 的 scrollXXXBy 方法中
if (dx != 0) {
consumedX = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
}
if (dy != 0) {
consumedY = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
}
//...
// 赋值
if (consumed != null) {
consumed[0] = consumedX;
consumed[1] = consumedY;
}
}
从上面源码部分中可以看出,RecyclerView 的滑动最终是交给 LayoutManager 去处理,我们以 LinearLayoutManager 为例继续跟踪源码:
LinearLayoutManager.java
public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler,
RecyclerView.State state) {
// ...
return scrollBy(dx, recycler, state);
}
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
RecyclerView.State state) {
// ...
return scrollBy(dy, recycler, state);
}
不论是水平还是垂直滑动都调用到了 scrollBy 方法,继续跟踪源码:
LinearLayoutManager.java
int scrollBy(int delta, RecyclerView.Recycler recycler, RecyclerView.State state) {
// ...
// 注意这里这个 fill 方法
final int consumed = mLayoutState.mScrollingOffset
+ fill(recycler, mLayoutState, state, false);
// ...
return scrolled;
}
fill 是填充的意思,当发生滑动时,就需要有新的 View 来填充滑动的部分,来看一下 fill 的源码:
LinearLayoutManager.java
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
RecyclerView.State state, boolean stopOnFocusable) {
final int start = layoutState.mAvailable;
if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
// ...
// 回收
recycleByLayoutState(recycler, layoutState);
}
int remainingSpace = layoutState.mAvailable + layoutState.mExtraFillSpace;
LayoutChunkResult layoutChunkResult = mLayoutChunkResult;
while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
// ...
// 复用
layoutChunk(recycler, state, layoutState, layoutChunkResult);
// ...
}
}
在 RecyclerView 滑动时回收和复用操作会同时触发,有移除屏幕的 ViewHolder 就需要有新的 ViewHolder 来填补,这个不难理解。
下面来分别看下回收和复用的方法调用流程。
recycleByLayoutState(recycler, layoutState);
LinearLayoutManager.java
private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) {
// ...
if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
// 回收头部 View
recycleViewsFromEnd(recycler, scrollingOffset, noRecycleSpace);
} else {
// 回收尾部 View 两个方法逻辑都一样
recycleViewsFromStart(recycler, scrollingOffset, noRecycleSpace);
}
}
// recycleViewsFromEnd 和 recycleViewsFromStart 逻辑整体一样 就只贴一个了
private void recycleViewsFromEnd(RecyclerView.Recycler recycler, int scrollingOffset,
int noRecycleSpace) {
final int childCount = getChildCount();
// ...
// 是否倒置 (LinearLayoutManager 构造器中可以设置)
// 无论 if 还是 else 最终都调用到了 recycleChildren
if (mShouldReverseLayout) {
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (mOrientationHelper.getDecoratedStart(child) < limit
|| mOrientationHelper.getTransformedStartWithDecoration(child) < limit) {
// 根据 i 来回收 View
recycleChildren(recycler, 0, i);
return;
}
}
} else {
for (int i = childCount - 1; i >= 0; i--) {
View child = getChildAt(i);
if (mOrientationHelper.getDecoratedStart(child) < limit
|| mOrientationHelper.getTransformedStartWithDecoration(child) < limit) {
// 根据 i 来回收 View
recycleChildren(recycler, childCount - 1, i);
return;
}
}
}
}
private void recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex) {
// ...
// 最终调用到了 removeAndRecycleViewAt 方法
if (endIndex > startIndex) {
for (int i = endIndex - 1; i >= startIndex; i--) {
removeAndRecycleViewAt(i, recycler);
}
} else {
for (int i = startIndex; i > endIndex; i--) {
removeAndRecycleViewAt(i, recycler);
}
}
}
LayoutManager.java
public void removeAndRecycleViewAt(int index, @NonNull Recycler recycler) {
final View view = getChildAt(index); //根据 index 找到 View
removeViewAt(index); // 从 RV 中移除
recycler.recycleView(view); // 交给 recycler 进行回收
}
layoutChunk(recycler, state, layoutState, layoutChunkResult);
LinearLayoutManager.java
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
LayoutState layoutState, LayoutChunkResult result) {
View view = layoutState.next(recycler);
// ...
}
View next(RecyclerView.Recycler recycler) {
if (mScrapList != null) {
return nextViewFromScrapList();
}
// 最终通过 recycler 的 getViewForPosition 方法获取到了 View
final View view = recycler.getViewForPosition(mCurrentPosition);
mCurrentPosition += mItemDirection;
return view;
}
Recycler
从上述的源码调用流程中可以看出,回收复用的具体逻辑都放在了 Recycler 中,Recycler 和 LayoutManager 一样是 RecyclerView 的内部类,先来看下 Recycler 中的一些定义:
RecyclerView.java
public final class Recycler {
// 一级缓存
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
ArrayList<ViewHolder> mChangedScrap = null;
// 二级缓存
final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
// 三级缓存
private ViewCacheExtension mViewCacheExtension;
// 四级缓存
RecycledViewPool mRecyclerPool;
// ...
}
在这里先对这些定义有个大致的了解:
- mAttachedScrap、mChangedScrap 屏幕上的 ViewHolder 缓存;
- mCachedViews 刚刚移除屏幕的 ViewHolder 会加入进来,默认容量为 2,超出容量后最先放入的会移出,并且加入 mRecyclerPool 缓存池中;
- mViewCacheExtension 供开发者自定义的缓存,一般不实现(在回收源码流程中并没有使用);
- mRecyclerPool 缓存池;
RecycledViewPool
mAttachedScrap、mCachedViews 就是简单的 ArrayList,存储 ViewHolder,RecycledViewPool 就需要查看源码来了解下了:
RecycledView.java
public static class RecycledViewPool {
private static final int DEFAULT_MAX_SCRAP = 5;
static class ScrapData {
final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
int mMaxScrap = DEFAULT_MAX_SCRAP; // mScrapHeap 的容量 默认为 5
long mCreateRunningAverageNs = 0;
long mBindRunningAverageNs = 0;
}
// 缓存池容器
SparseArray<ScrapData> mScrap = new SparseArray<>();
// ...
}
可以看出 RecycledViewPool 内部的容器为 SparseArray,存储类型是其内部类 ScrapData,而 ScrapData 内部使用 ArrayList 来存储 ViewHolder。当 RecyclerView 多类型 item 进行缓存时,直接以 itemViewType 作为 key,ScrapData 作为 value 对 ViewHolder 进行缓存,默认情况下每种不同的 ViewHolder 可以分别缓存 5 个。
以上部分对回收复用的源码调用流程以及四级缓存的容器有了一些简单了解,下面就来详细了解下回收复用的源码。
回收
回收源码从 Recycler.recycleView(view) 直接看起:
RecyclerView.java
public void recycleView(@NonNull View view) {
// View 的 LayoutParams 为 RecyclerView.LayoutParams
// 从其内部可以直接获取到 ViewHolder
ViewHolder holder = getChildViewHolderInt(view);
if (holder.isTmpDetached()) {
// 清除动画资源等 并且触发 adapter 的 onViewDetachedFromWindow 方法
removeDetachedView(view, false);
}
// 如果 ViewHolder 存在于 mAttachedScrap 或者 mChangedScrap 中就移除、清除相关 flag
if (holder.isScrap()) {
holder.unScrap();
} else if (holder.wasReturnedFromScrap()) {
holder.clearReturnedFromScrapFlag();
}
// 回收核心方法
recycleViewHolderInternal(holder);
// 停止动画
if (mItemAnimator != null && !holder.isRecyclable()) {
mItemAnimator.endAnimation(holder);
}
}
void recycleViewHolderInternal(ViewHolder holder) {
// ...
final boolean transientStatePreventsRecycling = holder
.doesTransientStatePreventRecycling();
// 这里提一下 可以重写 adapter 的 onFailedToRecycleView 方法返回 ture 来达到强制回收 ViewHolder
final boolean forceRecycle = mAdapter != null
&& transientStatePreventsRecycling
&& mAdapter.onFailedToRecycleView(holder);
boolean cached = false;
boolean recycled = false;
// ...
// 如果可以回收
if (forceRecycle || holder.isRecyclable()) {
if (mViewCacheMax > 0
&& !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
| ViewHolder.FLAG_REMOVED
| ViewHolder.FLAG_UPDATE
| ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
// 先获取 mCachedViews 的 size
int cachedViewSize = mCachedViews.size();
if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
// 超出容量后移除头部的 item
// 注意这个方法 !!! 内部还将移除的 ViewHolder 加入到了 RecyclerViewPool 中
recycleCachedViewAt(0);
cachedViewSize--;
}
int targetCacheIndex = cachedViewSize;
// 这个 if 中是预加载机制相关代码 后面解释
if (ALLOW_THREAD_GAP_WORK
&& cachedViewSize > 0
&& !mPrefetchRegistry.lastPrefetchIncludedPosition(holder.mPosition)) {
// when adding the view, skip past most recently prefetched views
int cacheIndex = cachedViewSize - 1;
while (cacheIndex >= 0) {
int cachedPos = mCachedViews.get(cacheIndex).mPosition;
if (!mPrefetchRegistry.lastPrefetchIncludedPosition(cachedPos)) {
break;
}
cacheIndex--;
}
targetCacheIndex = cacheIndex + 1;
}
// 添加到 mCachedViews 缓存中
mCachedViews.add(targetCacheIndex, holder);
cached = true;
}
if (!cached) {
// 如果没有添加到 mCachedView 中则直接加入 RecycledViewPool 中
addViewHolderToRecycledViewPool(holder, true);
recycled = true;
}
} else {
// ...
// 不能回收就什么也不做
}
// 清除保存的 ViewHolder 动画相关的信息
mViewInfoStore.removeViewHolder(holder);
if (!cached && !recycled && transientStatePreventsRecycling) {
holder.mBindingAdapter = null;
holder.mOwnerRecyclerView = null;
}
}
void recycleCachedViewAt(int cachedViewIndex) {
// 根据 index 获取到要从 mCachedViews 中移除的 ViewHolder
ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
// 添加到 RecycledViewPool
addViewHolderToRecycledViewPool(viewHolder, true);
// 从 mCachedViews 移除
mCachedViews.remove(cachedViewIndex);
}
void addViewHolderToRecycledViewPool(@NonNull ViewHolder holder, boolean dispatchRecycled) {
View itemView = holder.itemView;
// ...
if (dispatchRecycled) {
// 触发 adapter 的 onViewRecycled 方法
dispatchViewRecycled(holder);
}
// ...
// 添加到缓存池
getRecycledViewPool().putRecycledView(holder);
}
public void putRecycledView(ViewHolder scrap) {
// 根据 itemViewType 获取缓存池中对应的 ArrayList
// 没有的话会 new 一个 ScrapData 添加到 mScrap 中
final int viewType = scrap.getItemViewType();
final ArrayList<ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap;
// 判断是否超出容量
if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
return;
}
// ...
// 清除 ViewHolder 的状态信息
scrap.resetInternal();
// 添加到缓存池
scrapHeap.add(scrap);
}
注释中有详细的解释,在这里就简单总结下整体回收的流程:
- 如果要回收的 ViewHolder 存在于一级缓存 mAttachedScrap、mChangedScrap 中则从中移除;
- 将 ViewHolder 加入二级缓存 mCacheViews 中,如果容量超出则移除 index 为 0 的 ViewHolder,并且将其添加到 RecyclerViewPool 中;
- 如果 ViewHolder 在第 2 步中没有成功添加到 mCacheViews 中则直接添加进 RecyclerViewPool 缓存;
- RecyclerViewPool 中会根据 ItemViewType 获取对应 ViewHolder 的 ArrayList 容器(如果还没初始化则直接 new 一个)判断容量后添加进去,添加前会清除 ViewHolder 的一些状态信息;
复用
熟悉了回收流程后趁热打铁来看一下复用的源码流程,从 Recycler.getViewForPosition(position) 直接看起:
RecyclerView.java
public View getViewForPosition(int position) {
// 调用了重载方法
return getViewForPosition(position, false);
}
View getViewForPosition(int position, boolean dryRun) {
// 调用了 tryGetViewHolderForPositionByDeadline 获取到 ViewHolder
// 将 ViewHolder 的 itemView 返回
return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
}
// 复用机制的核心源码
ViewHolder tryGetViewHolderForPositionByDeadline(int position,
boolean dryRun, long deadlineNs) {
// ...
boolean fromScrapOrHiddenOrCache = false;
ViewHolder holder = null;
// 如果是预布局状态通过一级缓存 mChangedScrap 中查找
if (mState.isPreLayout()) {
holder = getChangedScrapViewForPosition(position);
fromScrapOrHiddenOrCache = holder != null;
}
// 二级缓存 内部会先从 mAttachedScrap 中尝试获取,获取不到则尝试从 mCacheViews 中寻找
// 注意这里是通过 position 寻找
if (holder == null) {
holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
// ...
}
// 二级缓存 跟上面的区别是这次根据 mHasStableIds 寻找
// adapter 重写 getItemId 方法设置 id
if (holder == null) {
// ...
if (mAdapter.hasStableIds()) {
holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
type, dryRun);
// ...
}
// 三级缓存 自定义缓存
if (holder == null && mViewCacheExtension != null) {
// 注意这里返回的是 View
final View view = mViewCacheExtension
.getViewForPositionAndType(this, position, type);
if (view != null) {
// 通过 RecyclerView.LayoutParams 获取其 ViewHolder
holder = getChildViewHolder(view);
// ...
}
}
// 四级缓存
if (holder == null) { // fallback to pool
holder = getRecycledViewPool().getRecycledView(type);
// ...
}
// 四级缓存都没有获取到
if (holder == null) {
// ...
// 通过 adapter onCreateViewHolder 方法创建 ViewHolder
holder = mAdapter.createViewHolder(RecyclerView.this, type);
// 预加载机制相关代码 后面解释
if (ALLOW_THREAD_GAP_WORK) {
// only bother finding nested RV if prefetching
RecyclerView innerView = findNestedRecyclerView(holder.itemView);
if (innerView != null) {
holder.mNestedRecyclerView = new WeakReference<>(innerView);
}
}
// ...
}
}
}
// ...
boolean bound = false;
if (mState.isPreLayout() && holder.isBound()) {
holder.mPreLayoutPosition = position;
} else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
// 如果没有绑定过数据 则调用 adapter onBindViewHolder 进行绑定
final int offsetPosition = mAdapterHelper.findPositionOffset(position);
bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
}
// 给 ViewHolder 的 itemView 设置 LayoutParams
final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
// 这里的 LayoutParams 是 RecyclerView.LayoutParams
final LayoutParams rvLayoutParams;
if (lp == null) {
// LayoutParams 由 LayoutManager 返回
rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
holder.itemView.setLayoutParams(rvLayoutParams);
} else if (!checkLayoutParams(lp)) {
rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
holder.itemView.setLayoutParams(rvLayoutParams);
} else {
rvLayoutParams = (LayoutParams) lp;
}
rvLayoutParams.mViewHolder = holder; // 将 ViewHolder 赋值给 LayoutParams
rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound;
return holder;
}
总结下,源码中四级缓存的方法分别为:
- 一级缓存 getChangedScrapViewForPosition,根据 position 从 mChangedScrap 中获取;
- 二级缓存 getScrapOrHiddenOrCachedHolderForPosition、getScrapOrCachedViewForId,优先根据 position 从 mAttachScrap、mCacheViews 中获取,其次根据 itemId 再次从 mAttachScrap、mCacheViews 中获取;
- 三级缓存 mViewCacheExtension.getViewForPositionAndType,自定义缓存,一定注意这里返回的是 View;
- 四级缓存 getRecycledViewPool().getRecycledView,根据 itemViewType 获取对应 ViewHolder 的缓存 ArrayList,再从其中尝试获取。
每级缓存对应的方法源码比较易懂,篇幅原因就不挨着贴出来了。
预加载
在了解与加载机制之前,需要先简单了解下 Android 开启硬件加速后的 UI 渲染流程:
UI Thread 将 Render Node 同步给 Render Thread 后等待的过程就是预加载要利用的空隙:
原理大概就是这样,具体来看一下源码中是如何实现:
RecyclerView.java
protected void onAttachedToWindow() {
// ...
// SDK >= 21
if (ALLOW_THREAD_GAP_WORK) {
// sGapWorker 是一个 ThreadLocal
mGapWorker = GapWorker.sGapWorker.get();
if (mGapWorker == null) { //初始化 GapWorker
mGapWorker = new GapWorker();
// ...
GapWorker.sGapWorker.set(mGapWorker);
}
mGapWorker.add(this);
}
}
GapWorker 实现了 Runnable 接口,GapWorker 的使用在 onTouchEvent 的 MOVE 事件中:
RecyclerView.java
public boolean onTouchEvent(MotionEvent e) {
// ...
switch (action) {
// ...
case MotionEvent.ACTION_MOVE: {
// ...
if (mGapWorker != null && (dx != 0 || dy != 0)) {
mGapWorker.postFromTraversal(this, dx, dy);
}
}
}
来看一下 postFromTraversal 源码:
GapWorker.java
void postFromTraversal(RecyclerView recyclerView, int prefetchDx, int prefetchDy) {
if (recyclerView.isAttachedToWindow()) {
// ...
if (mPostTimeNs == 0) {
mPostTimeNs = recyclerView.getNanoTime();
// GapWorker 实现了 Runnable
// 通过 View.post 执行 run 方法
recyclerView.post(this);
}
}
// ...
}
继续查看 GapWorker 的 run 方法实现:
GapWorker.java
public void run() {
try {
// Trace
Trace.beginSection(RecyclerView.TRACE_PREFETCH_TAG);
long lastFrameVsyncNs = TimeUnit.MILLISECONDS.toNanos(
mRecyclerViews.get(0).getDrawingTime());
if (lastFrameVsyncNs == 0) {
return;
}
// 获取下次 VSync 信号到来的时间
long nextFrameNs = lastFrameVsyncNs + mFrameIntervalNs;
// 尝试预加载
prefetch(nextFrameNs);
}
// ...
}
void prefetch(long deadlineNs) {
buildTaskList(); // 构建任务
flushTasksWithDeadline(deadlineNs); // 执行
}
private void flushTasksWithDeadline(long deadlineNs) {
for (int i = 0; i < mTasks.size(); i++) {
final Task task = mTasks.get(i);
if (task.view == null) {
break; // done with populated tasks
}
// 循环执行
flushTaskWithDeadline(task, deadlineNs);
task.clear();
}
}
private void flushTaskWithDeadline(Task task, long deadlineNs) {
long taskDeadlineNs = task.immediate ? RecyclerView.FOREVER_NS : deadlineNs;
// prefetchPositionWithDeadline 方法中进行了预加载
RecyclerView.ViewHolder holder = prefetchPositionWithDeadline(task.view,
task.position, taskDeadlineNs);
// ...
}
}
private RecyclerView.ViewHolder prefetchPositionWithDeadline(RecyclerView view,
int position, long deadlineNs) {
// ...
RecyclerView.Recycler recycler = view.mRecycler;
// 这里又调用到了 tryGetViewHolderForPositionByDeadline
RecyclerView.ViewHolder holder = recycler.tryGetViewHolderForPositionByDeadline(
position, false, deadlineNs);
// 预加载创建 View 成功之后
if (holder != null) {
if (holder.isBound()) { // 绑定数据成功 放入 mCacheViews 中
recycler.recycleView(holder.itemView);
} else { // 如果仅仅创建成功 没绑定数据 就放入 RecyclerViewPool 中
recycler.addViewHolderToRecycledViewPool(holder, false);
}
}
return holder;
}
再次回到 方法中:
ViewHolder tryGetViewHolderForPositionByDeadline(int position,
boolean dryRun, long deadlineNs) {
if (holder == null) {
long start = getNanoTime();
// 这里是根据 ViewHolder 的平均创建时间判断
// 判断是否有足够的时间进行预加载操作
if (deadlineNs != FOREVER_NS
&& !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
// abort - we have a deadline we can't meet
return null;
}
// 时间够 则预加载成功
holder = mAdapter.createViewHolder(RecyclerView.this, type);
// ...
}
// ...
// 利用剩余的时间尝试绑定数据
bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
// ...
}
可以看出预加载之后对 ViewHolder 的缓存和前面回收复用部分是相符合的。预加载机制一般不需要我们额外编写代码即可实现,这部分内容就当扩展了解吧。
最后
本博客的重点内容为 RecyclerView 的回收复用机制,预加载机制讲的并不是很详细,其中设计的 Android 渲染流程也是非常深的一项知识,后续有机会再分享吧。
如果我的博客分享对你有点帮助,不妨点个赞支持下!