Q1:RecyclerView 的四级缓存机制是什么?如何复用 ViewHolder?
答案核心
- 四级缓存(从上到下查找) :
- mAttachedScrap:屏幕内可见 ViewHolder,未分离,用于布局传递(不重建)。
- mCachedViews :刚滑出屏幕的 ViewHolder,默认容量 2,数据完全干净,无需重新绑定。
- mViewCacheExtension:开发者自定义缓存(极少使用)。
- mRecyclerPool :多列表共享缓存,按 itemViewType 分区,默认每区 5 个。从池中取出的 ViewHolder 会清理数据,必须重新绑定。
- 复用流程:LayoutManager 请求 View → 先查 scrap → 再查 cache → 再查自定义 → 最后从 pool 获取或创建。
流程图
精简源码
java
// Recycler 核心复用逻辑简化
ViewHolder tryGetViewHolderForPosition(...) {
// 1. scrap
holder = getScrapOrHiddenOrCachedHolderForPosition(position);
if (holder != null) return holder;
// 2. cache
holder = getScrapOrCachedViewForId(...);
if (holder != null) return holder;
// 3. pool
holder = getRecycledViewPool().getRecycledView(type);
if (holder != null) {
holder.resetInternal(); // 清空数据
return holder;
}
// 4. 创建新
holder = mAdapter.createViewHolder(parent, type);
return holder;
}
✨ 与后续 Q8(RecycledViewPool 共享)的「异」:Q1 讲四层完整结构,Q8 专门深化 pool 的跨列表复用。
Q2:RecyclerView 局部刷新原理?Payload 有什么用?
答案核心
- 局部刷新原理 :调用
notifyItemChanged(position, payload)后,RecyclerView 标记该 Item 为"脏",布局时仅刷新该 Item,不重建其他 Item。 - Payload 作用 :精准更新部分 UI ,避免全量绑定。
- 无 payload:执行完整的
onBindViewHolder(holder, position)。 - 有 payload:只调用
onBindViewHolder(holder, position, payloads),根据 payload 仅更新指定控件(如只改点赞数字)。
- 无 payload:执行完整的
流程图
精简源码
java
// Adapter 中实现带 payload 的绑定
@Override
public void onBindViewHolder(Holder holder, int position, List<Object> payloads) {
if (!payloads.isEmpty()) {
String payload = (String) payloads.get(0);
if ("UPDATE_LIKE".equals(payload)) {
holder.tvLike.setText(data.get(position).likeCount + "");
}
} else {
onBindViewHolder(holder, position); // 全量刷新
}
}
// 调用处
adapter.notifyItemChanged(position, "UPDATE_LIKE");
✨ 与 Q1 的「异」:Q1 解决"创建/复用"问题,Q2 解决"更新数据时如何高效刷新"。
Q3:DiffUtil 原理及使用场景?如何自动计算差异并局部刷新?
答案核心
- 原理:基于 Myers 差分算法(O(N+M)),比较新旧两个数据集,生成最小编辑操作(增、删、改、移)。
- 核心方法 :
areItemsTheSame:判断是否是同一个 item(通常用 id)。areContentsTheSame:判断内容是否变化(仅在 items same 时调用)。
- 场景 :替代手动
notifyItemXXX,尤其适合数据整体替换(如网络请求刷新列表),自动完成精准局部刷新。
流程图
精简源码
java
DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() {
@Override public boolean areItemsTheSame(int oldPos, int newPos) {
return oldList.get(oldPos).id == newList.get(newPos).id;
}
@Override public boolean areContentsTheSame(int oldPos, int newPos) {
return oldList.get(oldPos).equals(newList.get(newPos));
}
});
result.dispatchUpdatesTo(adapter);
adapter.setList(newList); // 更新内部数据
✨ 与 Q2 的「异」:Q2 是手动局部刷新,Q3 是自动化差异计算,适合大批量数据替换。
Q4:RecyclerView 卡顿如何优化?(综合方案)
答案核心
- 缓存优化 :增大
mCachedViews容量(setItemViewCacheSize(20)),共享RecycledViewPool。 - 刷新优化 :禁用
notifyDataSetChanged,使用局部刷新、Payload、DiffUtil。 - 布局优化 :Item 布局层级 ≤5,固定尺寸用
setHasFixedSize(true),关闭动画setItemAnimator(null)。 - 异步优化:图片用 Glide 缩略图 + 滑动暂停加载;复杂数据处理放子线程。
- 预加载 :
setItemPrefetchEnabled(true)(默认开启),自定义 LayoutManager 时实现collectAdjacentPrefetchPositions。
流程图
精简源码
java
// 综合优化示例
recyclerView.setItemViewCacheSize(20);
recyclerView.setRecycledViewPool(sharedPool);
recyclerView.setHasFixedSize(true);
recyclerView.setItemAnimator(null);
((LinearLayoutManager) recyclerView.getLayoutManager()).setItemPrefetchEnabled(true);
✨ 与后续 Q16(卡顿监控)、Q18(首屏速度)、Q19(滑动流畅)的「异」:Q4 是优化措施的全景图,后面三个是更细的"监控"、"首屏"、"丝滑"专项。
Q5:如何自定义 LayoutManager?必须实现哪些核心方法?
答案核心
- 必须实现的方法 :
generateLayoutParams():返回自定义 LayoutParams。onLayoutChildren():核心,布局所有子 View(通常先 detach + scrap,再 fill)。canScrollHorizontally()/canScrollVertically():支持滚动方向。scrollHorizontallyBy()/scrollVerticallyBy():处理滚动偏移,回收/添加 View。
- 关键流程 :
detachAndScrapAttachedViews(recycler)→ 根据锚点布局可见范围 →fill()填充剩余空间。
流程图(直线型 LayoutManager)
精简源码
java
public class MyLinearLayoutManager extends RecyclerView.LayoutManager {
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
detachAndScrapAttachedViews(recycler);
int offsetY = 0;
for (int i = 0; i < getItemCount(); i++) {
View view = recycler.getViewForPosition(i);
addView(view);
measureChildWithMargins(view, 0, 0);
layoutDecorated(view, 0, offsetY, getDecoratedMeasuredWidth(view),
offsetY + getDecoratedMeasuredHeight(view));
offsetY += getDecoratedMeasuredHeight(view);
}
}
@Override public boolean canScrollVertically() { return true; }
@Override public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
}
✨ 与 Q13(预布局 pre-layout)的「异」:Q5 是基础实现,Q13 深入讲解支持动画时必须处理的 pre-layout 阶段。
Q6:预取(Prefetch)机制如何提升滑动流畅度?
答案核心
- 定义:在惯性滑动(Fling)期间,RecyclerView 提前创建并缓存未来可能进入屏幕的 ViewHolder。
- 实现 :
GapWorker每一帧空闲时调用LayoutManager.collectAdjacentPrefetchPositions()获取预取位置列表,异步创建 ViewHolder 并放入mCachedViews。 - 对比上拉加载更多:Prefetch 是系统级 ViewHolder 预创建(缓存层),上拉加载是业务级数据预加载(数据层)。
- 优化 :自定义 LayoutManager 需实现
collectAdjacentPrefetchPositions;嵌套 RecyclerView 用setInitialPrefetchItemCount(4)。
流程图
精简源码
java
// 自定义 LayoutManager 参与预取
@Override
public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state,
LayoutPrefetchRegistry registry) {
int delta = (dy > 0) ? 1 : -1;
int firstPos = findFirstVisibleItemPosition();
for (int i = 1; i <= 3; i++) {
int pos = firstPos + delta * i;
if (pos >= 0 && pos < getItemCount()) registry.addPosition(pos, 0);
}
}
✨ 与 Q4(综合优化)的「异」:Q4 提到预加载但未展开,Q6 专门深度解析 Prefetch 原理。
Q7:嵌套滑动机制(NestedScrolling)原理?如何解决 RecyclerView 嵌套冲突?
答案核心
- 原理 :子 View(RecyclerView 已实现 NestedScrollingChild)滑动前先询问父 View(NestedScrollingParent),父子协同分配滚动距离。
startNestedScroll()→ 父响应。dispatchNestedPreScroll()→ 父先消耗。- 子消耗剩余 →
dispatchNestedScroll()传回未消耗部分。
- 解决冲突方案 :
- 简单粗暴:
recyclerView.setNestedScrollingEnabled(false)(子不再滚动,完全交给父)。 - 自定义父容器实现
NestedScrollingParent2接口,精细控制。 - 使用
CoordinatorLayout+Behavior(如 AppBarLayout 折叠)。
- 简单粗暴:
流程图
精简源码(父容器简化实现)
java
public class CustomParent extends FrameLayout implements NestedScrollingParent2 {
private NestedScrollingParentHelper helper = new NestedScrollingParentHelper(this);
@Override
public boolean onStartNestedScroll(@NonNull View child, @NonNull View target, int axes, int type) {
return (axes & View.SCROLL_AXIS_VERTICAL) != 0;
}
@Override
public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
int consumedY = Math.min(dy, mTopView.getHeight()); // 让顶部 View 先折叠
mTopView.offsetTopAndBottom(-consumedY);
consumed[1] = consumedY;
}
// 其他方法省略...
}
✨ 与 Q5(自定义 LayoutManager)的「异」:Q5 侧重布局管理,Q7 侧重事件分发与滑动协作。
Q8:RecycledViewPool 如何实现跨列表共享?与 mCachedViews 有何区别?
答案核心
- 作用 :存放超出
mCachedViews容量的 ViewHolder,按itemViewType分区存储,默认每区 5 个。 - 跨列表共享 :多个 RecyclerView 调用
setRecycledViewPool(pool)共用同一池子,减少onCreateViewHolder调用。 - 与 mCachedViews 区别 :
- mCachedViews:最多 2 个,ViewHolder 不清空数据,复用无需 re-bind。
- RecycledViewPool:容量更大,ViewHolder 会清理数据 (调用
resetInternal()),复用必须重新绑定。
流程图
精简源码
java
RecyclerView.RecycledViewPool sharedPool = new RecyclerView.RecycledViewPool();
recyclerViewA.setRecycledViewPool(sharedPool);
recyclerViewB.setRecycledViewPool(sharedPool);
sharedPool.setMaxRecycledViews(0, 20); // type 0 最多缓存20个
✨ 与 Q1 的「异」:Q1 讲四级缓存整体结构,Q8 深入最后一级 pool 的共享特性。
Q9:如何高效实现 ItemDecoration?画分割线时注意什么?
答案核心
- 核心方法 :
getItemOffsets(outRect, ...):为每个 item 预留绘制空间。onDraw(Canvas, ...):绘制在 item 下层(背景分割线)。onDrawOver(...):绘制在 item 上层(悬浮效果)。
- 性能注意 :
- 不在
getItemOffsets中创建对象(复用一个 Rect)。 - 避免在
onDraw中频繁分配 Paint/Drawable,预先初始化。 - 只绘制可见范围内的 item(遍历
parent.getChildCount())。
- 不在
流程图
精简源码
java
public class SimpleDivider extends RecyclerView.ItemDecoration {
private final Paint paint = new Paint();
private int dividerHeight = 1;
public SimpleDivider(int color) { paint.setColor(color); }
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if (parent.getChildAdapterPosition(view) != parent.getAdapter().getItemCount() - 1) {
outRect.bottom = dividerHeight;
}
}
@Override
public void onDraw(@NonNull Canvas canvas, RecyclerView parent, RecyclerView.State state) {
for (int i = 0; i < parent.getChildCount() - 1; i++) {
View child = parent.getChildAt(i);
canvas.drawRect(child.getLeft(), child.getBottom(), child.getRight(),
child.getBottom() + dividerHeight, paint);
}
}
}
✨ 与 Q4(卡顿优化)的「异」:Q9 是装饰绘制优化,属于视觉细节,而非滑动性能。
Q10:SnapHelper 如何实现滑动后自动对齐?(如 ViewPager 效果)
答案核心
- 作用:让 RecyclerView 滑动停止后自动对齐到某个 item 的特定位置。
- 官方实现 :
LinearSnapHelper(居中)、PagerSnapHelper(一次一页,类似 ViewPager)。 - 工作原理 :
- 重写
onFling()限制滑动速度和距离。 findSnapView()找到目标 View。calculateDistanceToFinalSnap()计算偏移量。smoothScrollBy()完成对齐。
- 重写
流程图
精简源码
java
// 一行代码实现 ViewPager 效果
new PagerSnapHelper().attachToRecyclerView(recyclerView);
// 自定义左对齐 SnapHelper
public class StartSnapHelper extends LinearSnapHelper {
@Override
public int[] calculateDistanceToFinalSnap(@NonNull LayoutManager lm, @NonNull View target) {
int[] out = new int[2];
out[0] = target.getLeft(); // 左边缘对齐
out[1] = 0;
return out;
}
}
✨ 与 Q7(嵌套滑动)的「异」:Q10 是交互体验优化,不涉及滚动冲突。
Q11:MergeAdapter 是什么?如何简化多类型列表开发?
答案核心
- 定义:AndroidX 1.2.0+ 提供的合并多个 Adapter 的工具,将多个独立 Adapter 按顺序串联成一个。
- 优势 :
- 解耦:每个业务模块有自己的 Adapter,不再需要一个 Adapter 内通过
getItemViewType写大量 if-else。 - 复用:相同类型的 Adapter 可在不同页面重复使用。
- 解耦:每个业务模块有自己的 Adapter,不再需要一个 Adapter 内通过
- 场景:Header + 列表 + Footer,或多个不同数据源拼接。
流程图
精简源码
java
Adapter header = new HeaderAdapter();
Adapter products = new ProductAdapter(productList);
Adapter footer = new FooterAdapter();
MergeAdapter mergeAdapter = new MergeAdapter(header, products, footer);
recyclerView.setAdapter(mergeAdapter);
// 单独更新产品部分
products.notifyItemChanged(0);
✨ 与 Q3(DiffUtil)的「异」:Q11 是结构解耦,Q3 是数据更新优化,两者可结合使用。
Q12:StaggeredGridLayoutManager(瀑布流)有哪些常见坑?如何解决?
答案核心
- Item 跳动/位置错乱 → 禁用间隙处理:
setGapStrategy(GAP_HANDLING_NONE)。 - 滑动到顶部回滑出现空白 → 调用
invalidateSpanAssignments()强制重算布局。 - DiffUtil + 瀑布流导致跳跃 → 刷新后手动
requestLayout()。 - 图片高度变化导致布局抖动 → 预先设置 ImageView 固定宽高比(如
android:scaleType="centerCrop"+Glide.override)。
流程图
精简源码
java
// 禁用间隙
StaggeredGridLayoutManager lm = new StaggeredGridLayoutManager(2, VERTICAL);
lm.setGapStrategy(StaggeredGridLayoutManager.GAP_HANDLING_NONE);
recyclerView.setLayoutManager(lm);
// DiffUtil 后强制刷新布局
diffResult.dispatchUpdatesTo(adapter);
recyclerView.post(() -> lm.invalidateSpanAssignments());
✨ 与 Q5(自定义 LayoutManager)的「异」:Q12 是系统自带 LayoutManager 的实战陷阱,面试常问"你遇到过什么问题"。
Q13:自定义 LayoutManager 时如何处理预布局(Pre-layout)?为什么需要两次布局?
答案核心
- 预布局定义 :当
supportsPredictiveItemAnimations() == true且数据变化时,onLayoutChildren会被调用两次:Pre-layout 和 Real-layout。 - 目的:Pre-layout 按旧数据布局,用于记录动画起始位置(如删除 item2 时,item5 应先出现在预布局中,以便执行平滑动画)。
- 处理方法 :通过
state.isPreLayout()区分两次布局,分别执行不同逻辑。
流程图
精简源码
java
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
if (state.isPreLayout()) {
layoutForPreLayout(recycler, state); // 按旧数据
} else {
detachAndScrapAttachedViews(recycler);
layoutForRealLayout(recycler, state); // 按新数据
}
}
@Override
public boolean supportsPredictiveItemAnimations() {
return true; // 告诉RecyclerView需要预布局
}
✨ 与 Q5(自定义 LayoutManager)的「异」:Q5 讲基础实现,Q13 深入支持动画的细节,是资深加分点。
Q14:ViewPager2 与 RecyclerView 如何配合实现 Banner + 列表联动?
答案核心
- 场景:顶部 ViewPager2 Banner,下方列表,上滑列表时 Banner 渐变消失。
- 推荐方案 :
CoordinatorLayout+AppBarLayout+CollapsingToolbarLayout。- ViewPager2 放在 AppBarLayout 内,RecyclerView 设置
app:layout_behavior="@string/appbar_scrolling_view_behavior"。
- ViewPager2 放在 AppBarLayout 内,RecyclerView 设置
- 嵌套滑动冲突 :ViewPager2 内部也是 RecyclerView,若内部有横向滑动,需调用
setNestedScrollingEnabled(false)避免冲突。 - 缓存池共享 :ViewPager2 中的多个 Fragment 内的 RecyclerView 可共享
RecycledViewPool。
流程图
精简源码
xml
<androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.appbar.AppBarLayout>
<com.google.android.material.appbar.CollapsingToolbarLayout>
<androidx.viewpager2.widget.ViewPager2 android:id="@+id/banner"/>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
✨ 与 Q7(嵌套滑动)和 Q8(缓存池共享)的「异」:Q14 是两者的综合应用场景,实际项目中非常常见。
Q15:ItemAnimator 动画原理?如何自定义?
答案核心
- 原理 :数据变化时,RecyclerView 记录每个 ViewHolder 动画前位置 → 重新布局记录动画后位置 →
ItemAnimator根据位置差执行动效。 - 默认动画 :
DefaultItemAnimator(淡入淡出、移动、删除)。 - 问题 :动画可能导致闪烁,可关闭
setItemAnimator(null)快速排查。 - 自定义 :继承
SimpleItemAnimator,重写animateAdd/Remove/Move/Change。
流程图
精简源码
java
// 自定义删除动画:淡出
public class FadeItemAnimator extends DefaultItemAnimator {
@Override
public boolean animateRemove(ViewHolder holder) {
holder.itemView.animate().alpha(0f).setDuration(200)
.withEndAction(() -> dispatchRemoveFinished(holder)).start();
return true;
}
}
recyclerView.setItemAnimator(new FadeItemAnimator());
✨ 与 Q2(局部刷新)的「异」:Q2 关注数据更新方式,Q15 关注更新时的视觉效果。
Q16:RecyclerView 卡顿如何监控和定位?有哪些工具?
答案核心
- 系统工具 :
Profile GPU Rendering:查看是否掉帧(超过 16ms 绿线)。Systrace/Perfetto:抓取 UI 线程,定位onBindViewHolder、onLayoutChildren耗时。Layout Inspector:检查过度绘制、View 层级。
- 代码埋点 :
- 自定义
OnScrollListener,记录帧间隔时间。 Choreographer.FrameCallback计算掉帧数。
- 自定义
- 线上监控 :
BlockCanary、Matrix等卡顿检测库。
流程图
精简源码(Choreographer 掉帧检测)
java
Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
long last = 0;
@Override
public void doFrame(long frameTimeNanos) {
if (last != 0) {
long diffMs = (frameTimeNanos - last) / 1_000_000;
if (diffMs > 16.6) Log.w("FPS", "掉帧 " + (diffMs / 16.6));
}
last = frameTimeNanos;
Choreographer.getInstance().postFrameCallback(this);
}
});
✨ 与 Q4(卡顿优化)的「异」:Q4 讲优化手段,Q16 讲定位手段,先监控后优化。
Q17:RecyclerView 闪烁问题有哪些原因?如何排查?
答案核心
- 常见原因 :
- 动画冲突 → 关闭
setItemAnimator(null)验证。 - 图片异步加载错位 → 未用 Glide 等自动处理生命周期。
- 局部刷新调用不当 →
notifyItemRangeChanged触发了不必要动画。 - DiffUtil 误判
areContentsTheSame返回 false 导致重建 View。
- 动画冲突 → 关闭
- 排查步骤 :
- 关闭动画 → 若不闪,动画问题。
- 检查图片加载回调是否用
setTag或 Glide。 - 打印
onBindViewHolder调用次数。
流程图
精简源码
java
// 错误用法:异步回调不校验位置
loadImage(url, bitmap -> holder.imageView.setImageBitmap(bitmap)); // 可能设错
// 正确:Glide 保证正确性
Glide.with(context).load(url).into(holder.imageView);
✨ 与 Q2(局部刷新)的「异」:Q2 讲如何刷新,Q17 讲刷新引起的异常现象。
Q18:如何提高 RecyclerView 首次加载速度?(首屏速度)
答案核心
- 减少初始布局计算 :提前 setAdapter,或用
setFixedSize(true)避免重复 measure。 - 异步预创建 ViewHolder :后台线程创建 ViewHolder 并预热到
RecycledViewPool(需特殊技巧)。 - 缓存池预热 :提前设置
setItemViewCacheSize(20)和setRecycledViewPool。 - 布局扁平化:Item 布局层级 ≤5。
- 分页加载:首屏只加载前 N 条数据,滚动后再加载更多。
流程图
精简源码(预热缓存池示例)
java
// 提前创建一些 ViewHolder 放入 pool(需要反射或临时RecyclerView)
// 更简单的方法:增大缓存 + 异步数据加载
recyclerView.setItemViewCacheSize(20);
recyclerView.setRecycledViewPool(sharedPool);
// 数据批量分页
adapter.setList(data.subList(0, Math.min(15, data.size())));
✨ 与 Q6(Prefetch)的「异」:Q6 是滑动过程中的预创建,Q18 是首次显示的加速。
Q19:如何让 RecyclerView 滑动更流畅(跟手性)?
答案核心
- 减少主线程计算 :
onBindViewHolder不做 IO、排序、数据库查询。 - 滑动时暂停非必要任务 :监听
SCROLL_STATE_DRAGGING时暂停图片加载、动画、日志。 - 布局优化 :避免
wrap_content父布局,固定 Item 尺寸用setHasFixedSize(true)。 - 预缓存 :
setItemPrefetchEnabled(true)(默认开启)。 - 抑制 GC :滚动中不创建新对象,复用
Rect、Paint等。
流程图
精简源码
java
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView rv, int newState) {
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
Glide.with(context).resumeRequests();
} else {
Glide.with(context).pauseRequests();
}
}
});
✨ 与 Q4(卡顿优化)的「异」:Q4 是综合方案,Q19 专注滑动跟手性、动态暂停任务。
Q20:如何实现 RecyclerView 的上拉加载更多?(数据预加载)
答案核心
- 原理:监听滑动状态,当最后一个可见 Item 距离数据总量小于阈值时,触发加载更多。
- 防抖 :用
isLoading标志位避免重复请求。 - 优化 :剩余 3 个 Item 时触发;惯性滑动时不立即触发,可
postDelayed等待滑动停止。 - 与 Prefetch(Q6)的区别:Prefetch 预创建 ViewHolder,上拉加载预加载数据。
流程图
精简源码
java
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
int threshold = 3;
boolean isLoading = false;
@Override
public void onScrolled(@NonNull RecyclerView rv, int dx, int dy) {
if (dy <= 0 || isLoading) return;
LinearLayoutManager lm = (LinearLayoutManager) rv.getLayoutManager();
int last = lm.findLastVisibleItemPosition();
if (last >= lm.getItemCount() - threshold) {
isLoading = true;
loadMore(() -> {
isLoading = false;
adapter.notifyItemRangeInserted(oldSize, newItems.size());
});
}
}
});
✨ 与 Q6(Prefetch)的「异」:Q6 是系统预创建 ViewHolder,Q20 是业务预加载数据。
总结:20题优先级排序速查表
| 优先级 | 问题编号 | 核心主题 |
|---|---|---|
| 最高 | Q1 | 四级缓存机制 |
| 高 | Q2 | 局部刷新 + Payload |
| 高 | Q3 | DiffUtil 自动差异 |
| 高 | Q4 | 卡顿综合优化方案 |
| 高 | Q5 | 自定义 LayoutManager |
| 中高 | Q6 | 预取 Prefetch |
| 中高 | Q7 | 嵌套滑动与冲突 |
| 中高 | Q8 | RecycledViewPool 共享 |
| 中 | Q9 | ItemDecoration |
| 中 | Q10 | SnapHelper 对齐 |
| 中 | Q11 | MergeAdapter 多类型 |
| 中 | Q12 | 瀑布流坑 |
| 中 | Q13 | 预布局 Pre-layout |
| 中 | Q14 | ViewPager2 联动 |
| 中 | Q15 | ItemAnimator 动画 |
| 中低 | Q16 | 卡顿监控工具 |
| 中低 | Q17 | 闪烁问题排查 |
| 中低 | Q18 | 首屏加载速度 |
| 中低 | Q19 | 滑动流畅度 |
| 中低 | Q20 | 上拉加载更多 |