Android知识总结
一、预加载流程
有RecyclerView.onTouchEvent
的ACTION_MOVE
事件进入分析
GapWorker mGapWorker;
public boolean onTouchEvent(MotionEvent e) {
case MotionEvent.ACTION_MOVE: {
if (mGapWorker != null && (dx != 0 || dy != 0)) {
//开始进入预加载过程
mGapWorker.postFromTraversal(this, dx, dy);
}
}
}
调用GapWorker
的postFromTraversal
方法。GapWorker
只要做间隙工作(即View 刷新间隙),对RecyclerView的item做预加载处理
final class GapWorker implements Runnable {
//GapWorker 的静态实例
static final ThreadLocal sGapWorker = new ThreadLocal<>();
//RecyclerView的缓存
ArrayList mRecyclerViews = new ArrayList<>();
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();
//会调用View的Post方法,通过Handler处理任务,活到Runable中
recyclerView.post(this);
}
}
recyclerView.mPrefetchRegistry.setPrefetchVector(prefetchDx, prefetchDy);
}
}
View.Post()
方法
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
getRunQueue().post(action);
return true;
}
执行GapWorker
的任务run
中执行
public void run() {
try {
TraceCompat.beginSection(RecyclerView.TRACE_PREFETCH_TAG);
if (mRecyclerViews.isEmpty()) {
// abort - no work to do
return;
}
final int size = mRecyclerViews.size();
long latestFrameVsyncMs = 0;
for (int i = 0; i < size; i++) {
RecyclerView view = mRecyclerViews.get(i);
if (view.getWindowVisibility() == View.VISIBLE) {
latestFrameVsyncMs = Math.max(view.getDrawingTime(), latestFrameVsyncMs);
}
}
if (latestFrameVsyncMs == 0) {
// abort - either no views visible, or couldn't get last vsync for estimating next
return;
}
long nextFrameNs = TimeUnit.MILLISECONDS.toNanos(latestFrameVsyncMs) + mFrameIntervalNs;
prefetch(nextFrameNs);
} finally {
mPostTimeNs = 0;
TraceCompat.endSection();
}
}
GapWorker.prefetch
方法
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;
RecyclerView.ViewHolder holder = prefetchPositionWithDeadline(task.view,
task.position, taskDeadlineNs);
if (holder != null
&& holder.mNestedRecyclerView != null
&& holder.isBound()
&& !holder.isInvalid()) {
prefetchInnerRecyclerViewWithDeadline(holder.mNestedRecyclerView.get(), deadlineNs);
}
}
private void prefetchInnerRecyclerViewWithDeadline(@Nullable RecyclerView innerView,
long deadlineNs) {
final LayoutPrefetchRegistryImpl innerPrefetchRegistry = innerView.mPrefetchRegistry;
innerPrefetchRegistry.collectPrefetchPositionsFromView(innerView, true);
...
}
回调到GapWorker
的内部类LayoutPrefetchRegistryImpl
的collectPrefetchPositionsFromView
方法
void collectPrefetchPositionsFromView(RecyclerView view, boolean nested) {
mCount = 0;
if (mPrefetchArray != null) {
Arrays.fill(mPrefetchArray, -1);
}
final RecyclerView.LayoutManager layout = view.mLayout;
if (view.mAdapter != null
&& layout != null
&& layout.isItemPrefetchEnabled()) {
if (nested) {
// nested prefetch, only if no adapter updates pending. Note: we don't query
// view.hasPendingAdapterUpdates(), as first layout may not have occurred
if (!view.mAdapterHelper.hasPendingUpdates()) {
layout.collectInitialPrefetchPositions(view.mAdapter.getItemCount(), this);
}
} else {
//1)、 需要预加载
if (!view.hasPendingAdapterUpdates()) {
layout.collectAdjacentPrefetchPositions(mPrefetchDx, mPrefetchDy,
view.mState, this);
}
}
if (mCount > layout.mPrefetchMaxCountObserved) {
//预加载 mPrefetchMaxCountObserved = 1
layout.mPrefetchMaxCountObserved = mCount;
//是否预加载了
layout.mPrefetchMaxObservedInInitialPrefetch = nested;
//2)、更新CacheSize的大小
view.mRecycler.updateViewCacheSize();
}
}
}
- 1)、步骤设置mCount的值
我们看LayoutManager
实现了LinearLayoutManager
的方法collectInitialPrefetchPositions
public void collectInitialPrefetchPositions(int adapterItemCount,
LayoutPrefetchRegistry layoutPrefetchRegistry) {
final boolean fromEnd;
final int anchorPos;
if (mPendingSavedState != null && mPendingSavedState.hasValidAnchor()) {
// use restored state, since it hasn't been resolved yet
fromEnd = mPendingSavedState.mAnchorLayoutFromEnd;
anchorPos = mPendingSavedState.mAnchorPosition;
} else {
resolveShouldLayoutReverse();
fromEnd = mShouldReverseLayout;
if (mPendingScrollPosition == RecyclerView.NO_POSITION) {
anchorPos = fromEnd ? adapterItemCount - 1 : 0;
} else {
anchorPos = mPendingScrollPosition;
}
}
final int direction = fromEnd
? LayoutState.ITEM_DIRECTION_HEAD
: LayoutState.ITEM_DIRECTION_TAIL;
int targetPos = anchorPos;
for (int i = 0; i < mInitialPrefetchItemCount; i++) {
if (targetPos >= 0 && targetPos < adapterItemCount) {
//预加载时,添加一个item值
layoutPrefetchRegistry.addPosition(targetPos, 0);
} else {
break; // no more to prefetch
}
targetPos += direction;
}
}
最后会回到GapWorker
中执行addPosition
方法
public void addPosition(int layoutPosition, int pixelDistance) {
if (layoutPosition < 0) {
throw new IllegalArgumentException("Layout positions must be non-negative");
}
if (pixelDistance < 0) {
throw new IllegalArgumentException("Pixel distance must be non-negative");
}
// allocate or expand array as needed, doubling when needed
final int storagePosition = mCount * 2;
if (mPrefetchArray == null) {
mPrefetchArray = new int[4];
Arrays.fill(mPrefetchArray, -1);
} else if (storagePosition >= mPrefetchArray.length) {
final int[] oldArray = mPrefetchArray;
mPrefetchArray = new int[storagePosition * 2];
System.arraycopy(oldArray, 0, mPrefetchArray, 0, oldArray.length);
}
// add position
mPrefetchArray[storagePosition] = layoutPosition;
mPrefetchArray[storagePosition + 1] = pixelDistance;
// mCount = 1,可以回到 collectPrefetchPositionsFromView方法中再看执行结果
mCount++;
}
- 2)、更新CacheSize的大小
执行RecyclerView
的updateViewCacheSize
方法
void updateViewCacheSize() {
int extraCache = mLayout != null ? mLayout.mPrefetchMaxCountObserved : 0;
//mViewCacheMax = 3
mViewCacheMax = mRequestedCacheMax + extraCache;
// first, try the views that can be recycled
//是否满足向 pool 中加入值
for (int i = mCachedViews.size() - 1;
i >= 0 && mCachedViews.size() > mViewCacheMax; i--) {
//向 pool 中加入VH
recycleCachedViewAt(i);
}
}
二、代码展示
下面是一个简单的代码来讲解预加载的打开或关闭效果
public class CheckCacheSizeActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
public static void startCheckCacheSizeActivity(Activity activity) {
Intent intent = new Intent(activity, CheckCacheSizeActivity.class);
activity.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_check_cache_size);
mRecyclerView = findViewById(R.id.cache_rv);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(RecyclerView.VERTICAL);
mRecyclerView.setLayoutManager(layoutManager);
mRecyclerView.setAdapter(new MyCheckCacheAdapter(initDate()));
//关闭预加载使能
layoutManager.setItemPrefetchEnabled(false);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Class> clazz = Class.forName("androidx.recyclerview.widget.LinearLayoutManager");
//获取预加载的个数
Field mPrefetchMaxCountObserved = clazz.getSuperclass()
.getDeclaredField("mPrefetchMaxCountObserved");
mPrefetchMaxCountObserved.setAccessible(true);
Log.e("--->", "mPrefetchMaxCountObserved = " + mPrefetchMaxCountObserved.get(layoutManager));
} catch (Exception e) {
e.printStackTrace();
}
try {
Class> clazz = Class.forName("androidx.recyclerview.widget.RecyclerView");
Field mRecyclerField = clazz.getDeclaredField("mRecycler");
mRecyclerField.setAccessible(true);
RecyclerView.Recycler recycler = (RecyclerView.Recycler) mRecyclerField.get(mRecyclerView);
Class> recyclerClass = recycler.getClass();
//获取缓存 mCachedViews
Field mCachedViews = recyclerClass.getDeclaredField("mCachedViews");
mCachedViews.setAccessible(true);
ArrayList views = (ArrayList) mCachedViews.get(recycler);
Log.e("--->", "mCachedViews.size = " + views.size());
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
private List initDate() {
List list = new ArrayList<>();
for (int i =0; i < 40; i ++){
list.add("This is item " + i);
}
return list;
}
class MyCheckCacheAdapter extends RecyclerView.Adapter {
private List mList;
public MyCheckCacheAdapter(List list) {
mList = list;
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.check_cache_rv_layout, null);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
holder.mTextView.setText(mList.get(position));
}
@Override
public int getItemCount() {
return mList != null ? mList.size() : 0;
}
class MyViewHolder extends RecyclerView.ViewHolder {
private TextView mTextView;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
mTextView = itemView.findViewById(R.id.name_tv);
}
}
}
}
2.1、打开预加载
layoutManager.setItemPrefetchEnabled(true);
打开预加载
- 效果
//预加载个数
E/--->: mPrefetchMaxCountObserved = 1
//mCachedViews 的大小
E/--->: mCachedViews.size = 3
2.2、关闭预加载
layoutManager.setItemPrefetchEnabled(false);
关闭预加载
- 效果
E/--->: mPrefetchMaxCountObserved = 0
E/--->: mCachedViews.size = 2
由上面可见 mCachedViews
的大小等于 DEFAULT_CACHE_SIZE + mPrefetchMaxCountObserved
的大小。