Android面试题汇总-RecyclerView、Fragment、WebView、性能优化等
一、RecyclerView
1、RecyclerView的多级缓存机制,每一级缓存具体作用是什么,分别在什么场景下会用到哪些缓存
RecyclerView的多级缓存机制是为了提高滚动和数据更新的效率而设计的。每一级缓存都有其特定的作用和使用场景。以下是各级缓存的作用和它们的使用场景:
- 一级缓存:mAttachedScrap 和 mChangedScrap
- 二级缓存:mCachedViews
- 三级缓存:ViewCacheExtension
- 四级缓存:RecycledViewPool
- 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的滑动回收复用机制是其核心功能之一,它允许应用高效地处理大量的数据集。这个机制确保了用户界面的流畅滚动,即使是在数据集非常大的情况下。
- 滑动和回收:
- 当用户滑动屏幕时,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的刷新回收复用机制是其核心特性之一,它允许应用程序高效地处理和显示大量数据。这个机制主要包括以下几个步骤:
- 刷新(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 } }
- mAttachedScrap: