Android navcation 主页缓存主页面不缓存子页面方案
在 Android 中实现主页三个 Fragment 及其数据画面的缓存,同时不缓存其他 Fragment,可以通过以下方案实现:
核心思路
- 使用
FragmentStatePagerAdapter
/FragmentPagerAdapter
管理主页的多个 Fragment - 结合 ViewModel + onSaveInstanceState 保存数据状态
- 使用
setOffscreenPageLimit
控制 Fragment 缓存数量 - Navigation 组件中设置
popUpTo
避免其他 Fragment 入栈时缓存
具体实现步骤
1. 主页 Fragment 容器布局 (activity_main.xml)
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
2. 主页 Activity 实现
class MainActivity : AppCompatActivity() {
private lateinit var viewPager: ViewPager2
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewPager = findViewById(R.id.view_pager)
setupViewPager()
}
private fun setupViewPager() {
// 创建适配器
val adapter = HomePagerAdapter(this)
viewPager.adapter = adapter
// 设置缓存页面数量(左右各缓存2页,实际缓存3页)
viewPager.offscreenPageLimit = 2
}
}
3. Fragment 适配器实现
class HomePagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) {
// 主页需要缓存的三个 Fragment
override fun getItemCount(): Int = 3
override fun createFragment(position: Int): Fragment {
return when (position) {
0 -> HomeFragment.newInstance()
1 -> DashboardFragment.newInstance()
2 -> NotificationsFragment.newInstance()
else -> throw IllegalArgumentException()
}
}
}
4. 示例 Fragment 实现(带数据缓存)
class HomeFragment : Fragment() {
private lateinit var viewModel: HomeViewModel
private var recyclerViewState: Parcelable? = null
companion object {
fun newInstance() = HomeFragment()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, s: Bundle?): View? {
return inflater.inflate(R.layout.fragment_home, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 初始化 ViewModel
viewModel = ViewModelProvider(this).get(HomeViewModel::class.java)
// 恢复列表滚动位置
recyclerView.layoutManager.onRestoreInstanceState(recyclerViewState)
// 观察数据变化
viewModel.data.observe(viewLifecycleOwner) { data ->
adapter.submitList(data)
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
// 保存列表滚动位置
recyclerViewState = recyclerView.layoutManager.onSaveInstanceState()
}
}
// ViewModel 保存数据
class HomeViewModel : ViewModel() {
private val _data = MutableLiveData<List<String>>()
val data: LiveData<List<String>> get() = _data
init {
loadData()
}
private fun loadData() {
// 从网络/数据库加载数据
_data.value = listOf("Item 1", "Item 2", "Item 3")
}
}
5. 导航到其他 Fragment 时的处理(避免缓存)
// 在需要跳转的地方
findNavController().navigate(
R.id.action_to_detail,
null,
navOptions {
// 关键配置:跳过主页缓存
popUpTo(R.id.main_graph) {
inclusive = false
saveState = false
}
}
)
关键配置说明
-
缓存控制机制:
-
offscreenPageLimit = 2
:确保左右各缓存2页(实际缓存当前页+左右页) -
FragmentStateAdapter
:自动处理 Fragment 生命周期和状态恢复
-
-
数据持久化:
- ViewModel:保存业务数据(配置更改时不丢失)
- onSaveInstanceState:保存 UI 状态(如列表滚动位置)
- Bundle 参数:保存简单数据(Fragment 重建时恢复)
-
非缓存 Fragment 配置:
navOptions { popUpTo(R.id.main_graph) { inclusive = false // 不清除主页 saveState = false // 不保存当前状态 } }
生命周期对比(验证缓存效果)
操作 | 主页 Fragment | 其他 Fragment |
---|---|---|
首次进入 | onCreateView | onCreateView |
切换到同页签 | 无变化 | - |
切换到其他页签 | onPause | - |
进入详情页 | onPause | onCreateView |
返回主页 | onResume | onDestroy |
旋转屏幕 | 仅恢复UI状态 | 完全重建 |
优化建议
-
数据加载策略:
viewModel.data.observe(viewLifecycleOwner) { data -> if (recyclerView.adapter == null) { // 首次初始化 initRecyclerView(data) } else { // 仅更新数据 adapter.submitList(data) } }
-
大图处理:
override fun onDestroyView() { // 释放图片资源 imageView.setImageDrawable(null) super.onDestroyView() }
-
内存监控:
adb shell dumpsys meminfo your.package.name
-
LeakCanary 检测:
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1'
此方案确保主页三个 Fragment 保持数据和 UI 状态,其他 Fragment 在离开时被销毁,优化了内存使用和用户体验。