android RecyclerView的一些优化点介绍

目录
  • 1.RecycledPool的重用
  • 2.setHasFixedSize(boolean)的使用
  • 3.setHasStableIds(boolean)的使用
  • 4.ViewCacheExtension的使用
  • 5.预加载
  • 6.更新列表的方式
    • item局部更新
    • 整体列表更新
  • 总结

1.RecycledPool的重用

场景以及使用:

多个RecyclerView出现,并且他们的item布局结构一致,这时候可以进行重用。

在进行RecyclerView的初始化设置时候进行RecycledPool的设置。

 //每个单元的视频列表的RecycledPool
    private var mRecycledViewPool: RecyclerView.RecycledViewPool? = null

unitVideoListContentRv.run {
                layoutManager = GridLayoutManager(itemView.context, 3)
                if (mRecycledViewPool != null) {
                    setRecycledViewPool(mRecycledViewPool)
                } else {
                    mRecycledViewPool = recycledViewPool
                }
                ...........
            }

重用前后的对比:

本次展示的是长列表中的item嵌套列表,进行多item的加载然后上下滑动,同时检测内存的开销占用。

列表的规模是13个item,每个item中有4个视频item,规模不算特别大。

重用RecycledPool之前:

数据加载完毕之后,最后上下滑动的内存趋于平稳在48.4m

重用RecycledPool之后:

数据加载完毕之后,最后上下滑动的内存趋于平稳在40m。

对比总结:

其实很明显可以看到内存开销减少,而内存开销减少能提升列表的流畅度,效果是显而易见的。这次的列表数据规模还不算大,后续如果规模增加到很大,那么对比将会更加明显。

2.setHasFixedSize(boolean)的使用

方法的名字就表明了,设置是否有固定的尺寸,就是说RecyclerView是否有固定的尺寸,如果设置了true。那么会在以下的情景用到:

onMeasure---测量

如果设置了true,那么RecyclerView的mHasFixedSize变量为true。

@Override
    protected void onMeasure(int widthSpec, int heightSpec) {
      if (mLayout == null) {
            defaultOnMeasure(widthSpec, heightSpec);
            return;
        }
      //是否允许自动测量
      if (mLayout.isAutoMeasureEnabled()) {
        .....
      } else {
        if (mHasFixedSize) { //是否有固定的尺寸
                mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
                return;
            }
        .........
      }
    }

// mLayout.onMeasure
 public void onMeasure(@NonNull Recycler recycler, @NonNull State state, int widthSpec,
                int heightSpec) {
            mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
        }

 void defaultOnMeasure(int widthSpec, int heightSpec) {
               //直接设置固定的宽度和高度,没有进行再次的测量
        final int width = LayoutManager.chooseSize(widthSpec,
                getPaddingLeft() + getPaddingRight(),
                ViewCompat.getMinimumWidth(this));
        final int height = LayoutManager.chooseSize(heightSpec,
                getPaddingTop() + getPaddingBottom(),
                ViewCompat.getMinimumHeight(this));

        setMeasuredDimension(width, height);
    }

所以设置了这个值的时候,RecyclerView在测量的时候会有性能上的提升。

3.setHasStableIds(boolean)的使用

方法的名称意思是设置是否有稳定的id,设置了该值为true后,ViewHolder中的mHasStableIds就为true。

StableId有三种模式:NO_STABLE_IDS、ISOLATED_STABLE_IDS、SHARED_STABLE_IDS

RecyclerView在进行Item的Remove,Insert,Change的时候会调用到。

如果设置了这个属性,那么需要在Adapter中重写getItemId(int position)方法。

这样子在进行列表的更新时候,Adapter会根据getItemId方法返回的long类型的id进行判断,决定当前的item是否需要刷新。因此取代以往的全部刷新的情况,从而提高效率。


class Album{
     String coverUrl;
     String title;
}

@Override
public long getItemId(int position){
    Album album = mListOfAlbums.get(position);
      //如果返回的id和上次不一样,那就代表这个item发生了数据变化,则进行刷新
      //如果返回的id和上次一致,那么这个item就没有改变,就无需刷新了。
    return (album.coverUrl + album.title).hashcode();
}

4.ViewCacheExtension的使用

它是一个静态抽象类,看类名就能大概知道view缓存扩展,类中包括方法:

/**
        返回一个能绑定到适配器position位置上的view
         * <p>
         * 此方法不应该创建新的视图。 相反,它期望返回一个已经创建的View,该View可以针对给定的类型和位置重                        新使用。 如果将视图标记为已忽略,则应先调用{@link LayoutManager#
              stopIgnoringView(View)},然后再返回视图。
         * RecyclerView将在必要时将返回的View重新绑定到该位置
         *
         * @param recycler The Recycler that can be used to bind the View
         * @param position The adapter position
         * @param type     The type of the View, defined by adapter
         * @return 绑定到给定位置的View;如果没有可重用的View,则为NULL
         * @see LayoutManager#ignoreView(View)
         */
public abstract View getViewForPositionAndType(@NonNull Recycler recycler, int position,
                int type);

该缓存为RecyclerView的第二级缓存,即如果开发者设置了该缓存,那么列表从CacheView中获取不到holder,就会从ViewCacheExtension从获取。

适用场景则为,列表有固定的数量条目和宽高,这样子,列表初始化的时候就能直接从这级缓存拿到ViewHolder,不需要再创建ViewHolder,大大节省时间,提高效率。

5.预加载

预加载功能在RecyclerView中是默认开启的。

public boolean onTouchEvent(MotionEvent e) {
   switch (action) {
       case MotionEvent.ACTION_MOVE: {
                      final int x = (int) (e.getX(index) + 0.5f);
                final int y = (int) (e.getY(index) + 0.5f);
                int dx = mLastTouchX - x;
                int dy = mLastTouchY - y;
                         if (mScrollState == SCROLL_STATE_DRAGGING) { //处于拖动状态
                    ........
                    if (mGapWorker != null && (dx != 0 || dy != 0)) { //滑动距离不等于0,
                        mGapWorker.postFromTraversal(this, dx, dy); //进行预取任务
                    }
                }
       }
       break;
   }
}

        /**
     * 在当前遍历之后立即安排预取。
     */
    void postFromTraversal(RecyclerView recyclerView, int prefetchDx, int prefetchDy) {
        if (recyclerView.isAttachedToWindow()) {
            ........
            //第一次触发拖动的是否将该runnable提交到Mainhandler里面,
            //等待UI thread执行完成再执行预取任务
            if (mPostTimeNs == 0) {
                mPostTimeNs = recyclerView.getNanoTime();//获取当前时间,记录改次任务的开始
                recyclerView.post(this); //提交当前任务
            }
        }
                //设置预加载的坐标
        recyclerView.mPrefetchRegistry.setPrefetchVector(prefetchDx, prefetchDy);
    }

         /**
     * 获取当前系统的时间,单位为纳秒
     */
        long getNanoTime() {
        if (ALLOW_THREAD_GAP_WORK) { //在Android5.0及以上的系统中
            return System.nanoTime(); //返回正在运行的Java虚拟机的高分辨率时间源的当前值,以纳秒为单位
        } else {
            return 0; //5.0以下的系统直接返回0
        }
    }

        /**
        在L +上,使用RenderThread,UI线程在将一帧传递给RenderThread之后但在下一帧开始之前具有空闲时间。我们        在此窗口中安排预取工作。
     */
    static final boolean ALLOW_THREAD_GAP_WORK = Build.VERSION.SDK_INT >= 21;

我们可以看下预加载程序的Runnable的run方法实现了什么操作。

@Override
    public void run() {
        try {
            TraceCompat.beginSection(RecyclerView.TRACE_PREFETCH_TAG);
                        //recyclerview嵌套的情况
            if (mRecyclerViews.isEmpty()) {
                // abort - no work to do
                return;
            }

                 //查询最新的vsync,以便于我们预测下一个
              //绘制时间在动画和输入的回调中未生效,所以在这里进行vsync的查询是安全的
            final int size = mRecyclerViews.size();
            long latestFrameVsyncMs = 0;
               //获取RecyclerView最近一次开始RenderThread的时间
            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) {
                //终止,没有任何视图可见,或者无法获得最新的vsync用于估计下一个
                return;
            }
                        //计算下一帧的时间,等于最新一帧的时间加上帧间隔的时间
              //事实上,这是预加载工作的最后期限时间,如果不能在这个时间之前完成,那就意味着预加载失败
            long nextFrameNs = TimeUnit.MILLISECONDS.toNanos(latestFrameVsyncMs) + mFrameIntervalNs;
                        //进行预加载
            prefetch(nextFrameNs);

            // TODO: consider rescheduling self, if there's more work to do
        } finally {
            mPostTimeNs = 0;
            TraceCompat.endSection();
        }
    }
void prefetch(long deadlineNs) {
              //建立任务列表
        buildTaskList();
              //在deadlineNs这个时间前执行并完成任务
        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();
        }
    }

6.更新列表的方式

item局部更新

单项item更新

notifyItemChanged(position)

notifyItemInserted(position)

notifyItemRemoved(position)

notifyItemMoved(fromPosition, toPosition)

整体列表更新

notifyDataSetChanged(慎用)

notifyItemRangeRemoved(positionStart, itemCount)

notifyItemRangeChanged(positionStart, itemCount)

notifyItemRangeInserted(positionStart, itemCount)

其它的优化点

过度绘制

如果列表中的一个Item存在过度绘制,那么列表所有的item都过度绘制,就到存在不必要的渲染工作,消耗系统资源。

防止过度绘制,可以打开开发者选项中的《调试GPU过度绘制》,查看页面中的颜色分区,然后进行对应的优化。

Android 将按如下方式为界面元素着色,以确定过度绘制的次数:

真彩色:没有过度绘制

蓝色:过度绘制 1 次

绿色:过度绘制 2 次

粉色:过度绘制 3 次

红色:过度绘制 4 次或更多次

因为在布局中同一帧多次绘制相同的像素就会发生绘制过度,因此修复过度绘制可以减少不必要的渲染工作,以此来提高性能。特别是对于大型,多列表的布局来说。

总结

到此这篇关于android RecyclerView的一些优化点介绍的文章就介绍到这了,更多相关android RecyclerView内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android RecyclerView实现滑动删除

    本文实例为大家分享了RecyclerView实现滑动删除的具体代码,供大家参考,具体内容如下 package com.example.demo; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.Lin

  • Android recyclerview实现纵向虚线时间轴的示例代码

    效果图 代码 package com.jh.timelinedemo; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.DashPathEffect; import android.graphics.Paint; import android.util.AttributeSet; import

  • Android实现的RecyclerView适配器

    这个适配器我珍藏已久(近两年), 不断看到别人发适配器相关的文章, 但我总觉得没我的好用, 所以今日拿出来分享(宣传)一下, 欢迎各位指正不足. 源码地址: GitHub 功能 无需继承 Adapter, 无需判断 item 类型. 支持页头和页脚. 支持自动展示空数据界面. 通过 Kotlin 的 lambda 大量缩减代码. 支持全局 Item 类型 支持 diff 刷新 使用 添加依赖 implementation "com.dengzii.adapter:$latestVersion&q

  • Android自定义RecyclerView Item头部悬浮吸顶

    本文实例为大家分享了Android自定义RecyclerView Item头部悬浮吸顶的具体代码,供大家参考,具体内容如下 概述 1.自定义了一个FrameLayout,引入条目的头部布局加入到自定义FrameLayout中. 2.将RecyclerView加入FrameLayout 3.条目头部View的Alpha动画以及设置透明和不透明这个时机大多是通过打log来确定的,硬推理还是有些难. 4.当屏幕显示区域的第二条Item距离控件顶端的距离小于条目头部View高度时,就开始移动条目头部Vi

  • Android RecyclerView网格布局示例解析

    一个简单的网格布局 activity_main.xml <?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/ap

  • Android入门教程之RecyclerView的具体使用详解

    目录 RecyclerView 的基本用法 横向滚动 RecyclerView 点击事件 RecyclerView 的基本用法 和我们之前学习的控件不一样,RecyclerView 属于新增控件,所以我们需要在项目的 build.gradle 中添加 RecyclerView 库的依赖,才能使用该控件 dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementa

  • android RecyclerView的一些优化点介绍

    目录 1.RecycledPool的重用 2.setHasFixedSize(boolean)的使用 3.setHasStableIds(boolean)的使用 4.ViewCacheExtension的使用 5.预加载 6.更新列表的方式 item局部更新 整体列表更新 总结 1.RecycledPool的重用 场景以及使用: 多个RecyclerView出现,并且他们的item布局结构一致,这时候可以进行重用. 在进行RecyclerView的初始化设置时候进行RecycledPool的设置

  • Android RecyclerView仿新闻头条的频道管理功能

    需要在build里添加依赖 compile 'com.android.support:recyclerview-v7:25.3.1' 布局文件activity_main <android.support.v7.widget.RecyclerView android:layout_weight="1" android:id="@+id/recyclerView_up" android:layout_width="match_parent" a

  • Android RecyclerView缓存复用原理解析

    目录 一.牵出缓存 1.缓存还在屏幕内的ViewHolder——Scrap缓存 mAttachedScrap mChangeScrap 用一个例子说明 2.缓存屏幕之外的ViewHolder——CacheView 3.mViewCacheExtension 4.RecycledViewPool 二.到底是四级缓存还是三级缓存 一.牵出缓存 都有哪些缓存,作用是什么,为什么这么设计 1.缓存还在屏幕内的ViewHolder——Scrap缓存 Scrap是RecyclerView中最轻量的缓存,它不

  • Android RecyclerView上拉加载和下拉刷新(基础版)

    这里讲述的是用谷歌原生的SwipeRefreshLayout,进行刷新,以及利用RecycleView的滚动事件,判断是否到最后一个item,进行加载更多,这里加载更多是在RecycleView的适配器中使用不同item进行完成的. 这是activity的xml布局: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.a

  • Android RecyclerView 数据绑定实例代码

    前言 在上一个项目里有很多很多很多很多的RecyclerView,然后我需要写很多很多很多很多的Adapter和Viewholder--多倒没问题,但是里面有很多重复的代码这就不能忍了!每一个Adapter和ViewHolder其实做的事情非常的像:视图绑定,数据绑定,点击事件分发.还有啥?既然它们做的事情都一样,为啥我们还要傻傻的继续写着重复的代码? 正文 BaseAdapter 通常我们要创建一个RecyclerView.Adapter是怎么做的? 接收一个数据列表 重写getItemCou

  • Android RecyclerView打造悬浮效果的实现代码

    本文介绍了Android RecyclerView悬浮效果,分享给大家,具体如下: 先看个效果 这是一个City列表,每个City都有所属的Province,需要在滑动的时候,将对应的Province悬浮在顶部.悬浮顶部的Province需要根据列表的滑动而适当改变位置,实现"顶上去"的效果. 实现思路: 利用RecyclerView.ItemDecoration绘制Province(就像绘制分割线一样) 同一组的City,只绘制一个Province 计算偏移,将当前Province固

  • Android RecyclerView详解之实现 ListView GridView瀑布流效果

     什么是RecyclerView RecyclerView 是Google推出的最新的 替代ListView.GridView的组件,RecyclerView是用来显示大量数据的容器,并通过有限数量的子View,来提高滚动时的性能. 与ListView不同,RecyclerView 不再负责布局,而是专注于布局复用.布局主要通过 LayoutManager来管理,目前提供了3种常用的布局管理: LinearLayoutManager 线性布局管理器 (ListView效果) GridLayout

  • Android RecyclerView 上拉加载更多及下拉刷新功能的实现方法

    RecyclerView 已经出来很久了,但是在项目中之前都使用的是ListView,最近新的项目上了都大量的使用了RecycleView.尤其是瀑布流的下拉刷新,网上吧啦吧啦没有合适的自己总结了一哈. 先贴图上来看看: 使用RecyclerView实现上拉加载更多和下拉刷新的功能我自己有两种方式: 1.使用系统自带的Android.support.v4.widget.SwipeRefreshLayout这个控价来实现. 2.自定义的里面带有RecyleView的控件. 使用RecycleVie

  • Android RecyclerView上拉加载更多功能回弹实现代码

    实现原理是使用RecyclerView的OnTouchListener方法监听滑动 在adapter里面增加两项footview 其中date.size为显示的加载条,可以自定义,date.size+1为空白的View,我们设置其高度为0 我们通过LinearLayoutManager的 findLastVisibleItemPosition判断显示的最后一条数据,如果是空白view,表示加载条已经完全展示,松开即可刷新. 回弹效果是通过在滑动时动态改变空白view的高度,达到阻尼效果 ,回弹时

  • Android RecyclerView 基础知识详解

    本周的谷歌I/O大会带来了很多关于Android的振奋人心的消息.可能我们需要较长的时间来消化Android L引入的新东西. 这些天我一直在研究RecyclerView,并想在此给各位分享一下到目前为止我的成果. RecyclerView是什么? RecyclerView是一种新的视图组,目标是为任何基于适配器的视图提供相似的渲染方式.它被作为ListView和GridView控件的继承者,在最新的support-V7版本中提供支持. 在开发RecyclerView时充分考虑了扩展性,因此用它

随机推荐