结合源码分析RecyclerView复用机制: Recycler

自从 RecyclerView 出现,很多开发者都替换 ListView/GridView,改用它了。

首先我们要知道 Recycler 是 RecyclerView 的一个内部类,里面包括了生成新View,复用旧View,回收View,重新绑定View等逻辑。我理解 Recycler 的复用回收机制,有4级缓存:

      1,mChangedScrap,当 preLayout 的时候,会从它这获取。

      2,mAttachedScrap,mCachedViews,这两个也就是很多地方所谓的 ScrapView。

      3,mViewCacheExtension,外部可以自定义的,但是也需要自己控制回收 View。

      4,mRecyclerPool,可以外部全局共享一个 RecyclerPool。

RecyclerView 的滑动、动画或者是 NotifyDataXXX,主要流程 dispatchLayout() -> mLayout.onLayoutChildren()。

主要逻辑实际在 LayoutManager,RecyclerView 只是有一套自己的回收复用的机制,对外提供接口,而 LayoutManager 才是操作填充整个列表的,所以才有了 LinearLayoutManager,GridLayoutManager之类的。

LinearLayoutManager 类的 onLayoutChildren 方法:

@Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        // layout algorithm:
        // 1) by checking children and other variables, find an anchor coordinate and an anchor
        //  item position.
        // 2) fill towards start, stacking from bottom
        // 3) fill towards end, stacking from top
        // 4) scroll to fulfill requirements like stack from bottom.
        // create layout state
        
        // ......省略代码
        //重绘前,重置下 ViewHolder的 falg
        processAdapterUpdatesAndSetAnimationFlags();
        
        // ......省略代码
        // 把 ChildView detach,或者remove,放到 缓存
        detachAndScrapAttachedViews(recycler);

        // ......省略代码
        // 填满整个列表,拿到 ViewHolder,绑定数据,放到RecyclerView里面
        fill(recycler, mLayoutState, state, false);
    }
private void processAdapterUpdatesAndSetAnimationFlags() {
        // setAdapter, notifyDataXXX的时候, mDataSetHasChangedAfterLayout = true;
        if (mDataSetHasChangedAfterLayout) {
            //.....省略代码
            //这里会把 所有ChildView的ViewHolder的flag全部add FLAG_INVALID
            markKnownViewsInvalid();
            //.....省略代码
        }
        //.....省略代码
}
        //实际就是遍历所有ChildView,放到缓存
        public void detachAndScrapAttachedViews(Recycler recycler) {
            final int childCount = getChildCount();
            for (int i = childCount - 1; i >= 0; i--) {
                final View v = getChildAt(i);
                scrapOrRecycleView(recycler, i, v);
            }
        }

        private void scrapOrRecycleView(Recycler recycler, int index, View view) {
            final ViewHolder viewHolder = getChildViewHolderInt(view);
            if (viewHolder.shouldIgnore()) {
                return;
            }
            //这里的判断决定了,是 detachView (detach,会被放到 mAttachedScrap,mChangedScrap)
            //还是 removeView(remove,会被放到 mCachedViews,mRecyclerPool)
            // 如果 setAdapter, notifyDataXXX 的时候,ViewHolder 会是 needsUpdate | isInvalid;
            // notifyDataRemove的时候,会是 isRemoved;
            // ItemAnimation 有动画的时候,会是 isChanged;
            // mAdapter.hasStableIds(),就表示不存在View绑定内容无效的可能;
            if (viewHolder.isInvalid() && !viewHolder.isRemoved() && !viewHolder.isChanged() &&
                    !mRecyclerView.mAdapter.hasStableIds()) {
                removeViewAt(index);
                recycler.recycleViewHolderInternal(viewHolder);
            } else {
                detachViewAt(index);
                recycler.scrapView(view);
            }
         }

如果是removeView,会调用 RecyclerView 类的 recycleViewHolderInternal 方法做缓存:

void recycleViewHolderInternal(ViewHolder holder) {
            //.....省略代码
            boolean cached = false;
            boolean recycled = false;
            if (!holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED |
                    ViewHolder.FLAG_CHANGED | ViewHolder.FLAG_UPDATE)) {
                // mCachedViews 本身有容量限制,不可能无限制缓存View
                // 如果缓存 ViewHolder 时发现超过了 mCachedView 的限制,会将最老的 ViewHolder 移到 RecycledViewPool
                final int cachedViewSize = mCachedViews.size();
                if (cachedViewSize == mViewCacheMax && cachedViewSize > 0) {
                    recycleCachedViewAt(0);
                }
                if (cachedViewSize < mViewCacheMax) {
                    mCachedViews.add(holder);
                    cached = true;
                }
            }
            if (!cached) {
                // mCachedView 缓存失败, 会移到 RecycledViewPool
                addViewHolderToRecycledViewPool(holder);
                recycled = true;
            }
            //.....省略代码
 }

上面 recycleCachedViewAt方法传的是0,所以是将最老的 ViewHolder 移到 RecycledViewPool。

void recycleCachedViewAt(int cachedViewIndex) {
            if (DEBUG) {
                Log.d(TAG, "Recycling cached view at index " + cachedViewIndex);
            }
            ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
            if (DEBUG) {
                Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);
            }
            addViewHolderToRecycledViewPool(viewHolder);
            mCachedViews.remove(cachedViewIndex);
}

如果是 detachView,会调用 RecyclerView 类的 scrapView 方法做缓存:

void scrapView(View view) {
            final ViewHolder holder = getChildViewHolderInt(view);
            holder.setScrapContainer(this);
            // 如果是动画,会放到 mChangedScrap;
            if (!holder.isChanged() || !supportsChangeAnimations()) {
                mAttachedScrap.add(holder);
            } else {
                if (mChangedScrap == null) {
                    mChangedScrap = new ArrayList<ViewHolder>();
                }
                mChangedScrap.add(holder);
            }
 }

好了,上面就是把 ViewHolder 放到缓存里面的过程。

下面再来看怎么取缓存的ViewHolder:

LayoutManager类的 fill -> layoutChunk -> next ,再到 RecyclerView 类的 getViewForPosition方法:

        View getViewForPosition(int position, boolean dryRun) {
            //.....省略代码
            boolean fromScrap = false;
            ViewHolder holder = null;
            // 如果是 preLayout,从 mChangedScrap 取
            // Item动画的时候,会是 preLayout 状态
            if (mState.isPreLayout()) {
                //.....省略代码
                holder = getChangedScrapViewForPosition(position);
                //.....省略代码
            }
            if (holder == null) {
                // 先从 mAttachedScrap 取,如果没有,再从 mCachedViews 取
                holder = getScrapViewForPosition(position, INVALID_TYPE, dryRun);
                //.....省略代码
            }
            //.....省略代码
            if (holder == null) {
                if (holder == null && mViewCacheExtension != null) {
                    //.....省略代码
                    // 从 mViewCacheExtension 取
                    final View view = mViewCacheExtension.getViewForPositionAndType(this, position, type);
                }
                if (holder == null) {
                    //.....省略代码
                    // 从 mRecycledViewPool 取
                    holder = getRecycledViewPool().getRecycledView(type);
                    //.....省略代码
                }
                //.....省略代码
                if (holder == null) {
                    holder = mAdapter.createViewHolder(RecyclerView.this, type);
                }
            }
            //.....省略代码
            boolean bound = false;
            if (mState.isPreLayout() && holder.isBound()) {
                holder.mPreLayoutPosition = position;
            } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
                //.....省略代码
                //是否 bindViewHolder,绑定数据,ViewHolder的flag来判断
                // 如果 setAdapter, notifyDataXXX 的时候,ViewHolder 会是 needsUpdate | isInvalid;
                // 如果 滑出范围外 的时候,ViewHolder 会是 isBound;
                mAdapter.bindViewHolder(holder, offsetPosition);
                //.....省略代码
            }
            //.....省略代码
            // 得到 LayoutParams,赋值 mViewHolder
            final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
            final LayoutParams rvLayoutParams;
            if (lp == null) {
                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;
            //.....省略代码
            return holder.itemView;
        }

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值