Android面试题汇总-RecyclerView、Fragment、WebView、性能优化等

07-12 1778阅读

一、RecyclerView

1、RecyclerView的多级缓存机制,每一级缓存具体作用是什么,分别在什么场景下会用到哪些缓存

RecyclerView的多级缓存机制是为了提高滚动和数据更新的效率而设计的。每一级缓存都有其特定的作用和使用场景。以下是各级缓存的作用和它们的使用场景:

Android面试题汇总-RecyclerView、Fragment、WebView、性能优化等
(图片来源网络,侵删)
  • 一级缓存:mAttachedScrap 和 mChangedScrap
  • 二级缓存:mCachedViews
  • 三级缓存:ViewCacheExtension
  • 四级缓存:RecycledViewPool
    1. mAttachedScrap:
      • 作用:这是一个ArrayList,用于存储暂时分离的子视图(Scrap view)。这些视图可以重复使用而不需要与RecyclerView完全分离。如果视图不需要重新绑定数据,就不会进行修改。
      • 使用场景:在布局期间和数据更新时,这个缓存会被使用。
      • mChangedScrap:
        • 作用:类似于mAttachedScrap,但它存放的是发生变化的ViewHolder。如果使用到了这里的缓存的ViewHolder,需要重新走Adapter的绑定方法。
        • 使用场景:在数据有局部更新时使用。
        • mCachedViews:
          • 作用:这是一个有容量限制的ArrayList,默认大小为2。它存放的是已经从RecyclerView中移除的视图,但ViewHolder仍然保存着之前的信息,如位置和绑定的数据等。
          • 使用场景:在滚动过程中和预取(prefetch)时使用。
          • RecycledViewPool:
            • 作用:这是一个全局的缓存池,可以跨多个RecyclerView共享。它用于存储不再需要的ViewHolder,任何绑定过的痕迹都没有了。当需要新的item时,可以从这个池中获取,并进行重用。
            • 使用场景:在Item被移除、有更新或滚动过程中使用。
            • ViewCacheExtension:
              • 作用:这是一个开发者可以自定义的缓存层级。官方没有默认实现,它允许开发者根据自己的需求来缓存ViewHolder。
              • 使用场景:取决于开发者如何实现它。

    通过有效地管理ViewHolder的缓存和重用,RecyclerView的多级缓存机制极大地提高了处理大量数据集时的性能和效率。

    2、RecyclerView的滑动回收复用机制

    RecyclerView的滑动回收复用机制是其核心功能之一,它允许应用高效地处理大量的数据集。这个机制确保了用户界面的流畅滚动,即使是在数据集非常大的情况下。

    1. 滑动和回收:
      • 当用户滑动屏幕时,RecyclerView会检测到哪些项(item views)不再可见,并将这些项的ViewHolder回收到缓存中。这个过程称为回收(Recycling)。
      • 回收的ViewHolder会被存放在一个叫做mCachedViews的缓存列表中。这个列表有一个固定的大小,当达到上限时,最早回收的ViewHolder会被移除并放入另一个缓存池,即RecycledViewPool。
      • 复用:
        • 当新的项需要显示在屏幕上时,RecyclerView会首先尝试从mCachedViews中复用ViewHolder。如果找到了合适的ViewHolder,就会直接使用它,而不需要重新创建一个新的。
        • 如果mCachedViews中没有可用的ViewHolder,RecyclerView会转而从RecycledViewPool中寻找。RecycledViewPool是一个更大的缓存池,可以跨多个RecyclerView实例共享。
        • 绑定数据:
          • 一旦找到了一个可复用的ViewHolder,RecyclerView会通过调用Adapter的onBindViewHolder方法来绑定新的数据。这个过程称为绑定(Binding)。
          • 绑定数据是必要的步骤,因为虽然ViewHolder可能是复用的,但显示的数据需要是当前项的数据。
          • LayoutManager的角色:
            • LayoutManager负责决定屏幕上项的布局方式。它也参与回收和复用的过程,因为它知道哪些项不再可见,以及何时需要新的项来填充屏幕。
            • 优化:
              • RecyclerView还包含了其他优化措施,比如预取(Prefetching),它会在后台线程中提前绑定数据,以减少滚动时的延迟。

    通过这种方式,RecyclerView能够快速地回收和复用ViewHolder,从而实现高效的滚动性能。这个机制是Android开发中非常重要的优化手段,它使得即使是数据量巨大的列表也能够流畅地滚动。

    3、RecyclerView的刷新回收复用机制

    RecyclerView的刷新回收复用机制是其核心特性之一,它允许应用程序高效地处理和显示大量数据。这个机制主要包括以下几个步骤:

    1. 刷新(Refresh):
      • 当数据集发生变化时,例如通过notifyDataSetChanged()方法,RecyclerView会被通知需要刷新。
      • 刷新操作会导致RecyclerView重新绑定和布局视图,但尽可能地利用已有的ViewHolder进行数据绑定,以避免不必要的视图创建。
      • 回收(Recycle):
        • 当滑动RecyclerView时,屏幕上不再可见的视图会被回收到缓存中。
        • 回收的视图不会立即被销毁,而是存放在缓存中,等待复用。
        • 复用(Reuse):
          • 当需要显示新的数据项时,RecyclerView会首先尝试从缓存中查找可复用的ViewHolder。
          • 如果缓存中有合适的ViewHolder,就会直接使用它并绑定新的数据,而不是创建一个新的ViewHolder。

    RecyclerView的回收复用机制涉及到几个关键的缓存结构:

    • mCachedViews: 这是一个临时缓存,用于存放最近被回收的ViewHolder。它的默认大小为2,但可以根据需要调整。
    • RecycledViewPool: 这是一个更大的缓存池,用于存放不同类型的ViewHolder。它允许不同的RecyclerView共享ViewHolder,从而提高复用效率。
    • ViewCacheExtension: 这是一个可选的缓存扩展点,开发者可以自定义缓存策略,以适应特定的复用需求。

      在刷新过程中,RecyclerView会尽量复用已有的ViewHolder,减少创建和销毁视图的开销,从而提高性能和流畅度。这个机制确保了即使在数据频繁更新的情况下,用户界面也能保持流畅的滚动体验。

      4、RecyclerView 为什么要预布局

      RecyclerView 的预布局用于 Item 动画中,也叫做预测动画。其用于当 Item 项进行变化时执行的一次布局过程(如添加或删除 Item 项),使 ItemAnimator 体验更加友好。

      考虑以下 Item 项删除场景,屏幕内的 RecyclerView 列表包含两个 Item 项:item1 和 item2。当删除 item2 时,item3 从底部平滑出现在 item2 的位置:

      +-------+                       +-------+
      |       | 
          public MyFragment(String parameter) {
              // 将参数保存起来
          }
      }
      
          public static MyFragment newInstance(String parameter) {
              MyFragment myFragment = new MyFragment();
              Bundle args = new Bundle();
              args.putString("someParameter", parameter);
              myFragment.setArguments(args);
              return myFragment;
          }
      }
      
          private boolean isDataLoaded = false; // 标记数据是否已加载
          private boolean isFragmentVisible = false; // 标记 Fragment 是否可见
          // 其他生命周期方法...
          @Override
          public void onResume() {
              super.onResume();
              if (!isDataLoaded && isFragmentVisible) {
                  loadData(); // 加载数据
                  isDataLoaded = true;
              }
          }
          @Override
          public void setUserVisibleHint(boolean isVisibleToUser) {
              super.setUserVisibleHint(isVisibleToUser);
              isFragmentVisible = isVisibleToUser;
              if (!isDataLoaded && isVisibleToUser) {
                  loadData(); // 加载数据
                  isDataLoaded = true;
              }
          }
          private void loadData() {
              // 执行数据加载操作,如从网络获取数据、查询数据库等
              // 更新 UI
          }
      }
      
VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]