浅析RecyclerView预加载RV-Prefetch 机制

浅析RecyclerView预加载RV-Prefetch 机制

UI渲染基本流程(UI-Thread,Render-Thread,SurfaceFlinger)(硬件加速开启)

当系统V-Sync信号来临时,会唤醒主线程,回调编舞者Choreographer#FrameDisplayEventReceiver#onVsync()开始这一帧的绘制,依次处理input \ animation \ measure \ layout \ draw , 然后同步syncRenderThread(将这些操作整合成RenderNode),然后RenderThread将这些绘制指令转成OpenGL指令,最终通过GPU执行绘制到Graphic Buffer,然后SurfaceFlinger拿到buffer后进行layer的整合,最终把数据交给HAL层绘制到屏幕上。

在这里插入图片描述

什么时候预加载?加载什么?

我们先来看一下系统处理V-SYNCSystem-Trace

在这里插入图片描述

可以看到,俩次V-SYNC处理中是有主线程空闲状态的。为了更好丝滑的滑动,除了RV的缓存机制,其中还有一个预加载机制。缓存机制就是缓存VH,防止每一次都需要创建VH(也就是创建View),那么在上面的图中,能不能在不影响主线程的流程情况下,提前创建呢?很显然,在俩次处理中间,有一个等待V-SYNC来临的间隔时间—gap,就是在这一段里,提前尝试创建VH

预加载:RV-Prefetch

默认情况下,RV的预加载是开启的,现在让我们滑动RV,看下系统是怎么处理的(使用AS自带的Profile):

在这里插入图片描述

可以看到,在俩次V-SYNC处理之间,原本空闲的主线程,处理一个新任务—预加载

我们来跟踪一下代码,从滑动开始,即RecyclerViewonTouchEvent()中处理ACTION_MOVE

RecyclerView 1.2.1

 // 21以上默认开启
static final boolean ALLOW_THREAD_GAP_WORK = Build.VERSION.SDK_INT >= 21;

// onAttachToWindow()时初始化
// GapWork:一个Runnable,见缝插针
GapWorker mGapWorker;

@Override
protected void onAttachedToWindow() {
    // ...
    if (ALLOW_THREAD_GAP_WORK) {
        // Register with gap worker
        mGapWorker = GapWorker.sGapWorker.get();
        if (mGapWorker == null) {
            mGapWorker = new GapWorker();
            //...
        }
    }
}

@Override
public boolean onTouchEvent(MotionEvent e) {
	switch (action) {
         case MotionEvent.ACTION_MOVE: {
             // ....
             
             if (mGapWorker != null && (dx != 0 || dy != 0)) {
                 // 检测到滑动,借RecyclerView,post一个runnable到doFrame后面,也就是上面的RV-Prefetch
                  mGapWorker.postFromTraversal(this, dx, dy);
             }
         }
    }
}

在这里插入图片描述

来瞅瞅GapWorker#run()里干了什么:

	@Override
    public void run() {
        try {
            // 一开始就做了trace标记  "RV Prefetch"
            TraceCompat.beginSection(RecyclerView.TRACE_PREFETCH_TAG);

            // ...
		   // 下一次信号来临的时间
            long nextFrameNs = TimeUnit.MILLISECONDS.toNanos(latestFrameVsyncMs) + mFrameIntervalNs;
            // 调用prefetch()函数,预加载
            prefetch(nextFrameNs);

            // TODO: consider rescheduling self, if there's more work to do
        } finally {
            mPostTimeNs = 0;
            TraceCompat.endSection();
        }
    }

预加载核心流程:

1.判断需要预加载那种类型的ViewHolder

2.获取该类型的ViewHolder历史平均创建耗时 t1

3.获取距离下一次V-SYNC来临时的时间 t2

4.判断 t1是否小于t2,即能不能赶在下一次信号来临时创建ViewHolder成功

5.满足4,则创建该类型的ViewHoldewr

6.在5完成后,如果距离下一次信号来临时,还有时间,则尝试绑定ViewHolder

GapWorker

private RecyclerView.ViewHolder prefetchPositionWithDeadline(RecyclerView view,
            int position, long deadlineNs) {
        // ....

        RecyclerView.Recycler recycler = view.mRecycler;
        RecyclerView.ViewHolder holder;
        try {
            view.onEnterLayoutOrScroll();
            
            // 获取VH   tryGetViewHolderForPositionByDeadline
            holder = recycler.tryGetViewHolderForPositionByDeadline(
                    position, false, deadlineNs);

            // 如果预加载成功,根据情况存入mCachedViews或mRecyclerPool
            if (holder != null) {
                if (holder.isBound() && !holder.isInvalid()) {
                    // 已经绑定了数据,则存入 mCachedViews
                    recycler.recycleView(holder.itemView);
                } else {
                    // 没有绑定数据则存入 mRecylerPool
                    recycler.addViewHolderToRecycledViewPool(holder, false);
                }
            }
        } finally {
            view.onExitLayoutOrScroll(false);
        }
        return holder;
    }

RecyclerView.Recycler.tryGetViewHolderForPositionByDeadline()

@Nullable
ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs) {
    // ...
    
    ViewHolder holder = null;
    
    // ...
    
    if (holder == null) {
    // ....
                if (holder == null) {
                    long start = getNanoTime();
                    
                    // mRecyclerPool.willCreateInTime(type, start, deadlineNs) 能不能赶得及
                    if (deadlineNs != FOREVER_NS
                            && !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
                        // abort - we have a deadline we can't meet
                        // 不能的话,直接返回
                        return null;
                    }
                    
                    // 赶得及话,创建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);
                        }
                    }

                    long end = getNanoTime();
                    mRecyclerPool.factorInCreateTime(type, end - start);
                    if (DEBUG) {
                        Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder");
                    }
                }
            }
    
    //..
    
    		boolean bound = false;
            if (mState.isPreLayout() && holder.isBound()) {
                // do not update unless we absolutely have to.
                holder.mPreLayoutPosition = position;
            } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
               // ..
                final int offsetPosition = mAdapterHelper.findPositionOffset(position);
                // 还有剩余时间,尝试再绑定一下
                bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
            }
}

OK,RecyclerView预加载机制核心流程就这些,更加详细的预加载配置可以在LayoutManager中寻找(开启,加载个数等)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值