You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Jetpack Navigation实现Fragment预加载与切换不销毁(仿YouTube)

实现Jetpack Navigation下Fragment不销毁+ViewModel+Room预加载方案

一、阻止Fragment切换时销毁:启用keepAlive属性

Jetpack Navigation默认切换底部导航Fragment时会销毁前一个实例,要保留Fragment,只需在导航图中给对应Fragment目的地添加app:keepAlive="true"属性:

<fragment
    android:id="@+id/homeFragment"
    android:name="com.example.HomeFragment"
    android:label="Home"
    app:keepAlive="true">
</fragment>

该属性会让Fragment切换时仅执行onPause()/onResume(),不会触发onDestroyView()或onDestroy(),RecyclerView的视图状态与内容会被完整保留。

二、ViewModel+Room实现数据预加载与持久化

ViewModel的生命周期与宿主(如Activity/NavHostFragment)绑定,不会随Fragment生命周期销毁,结合Room可实现数据的预加载与本地缓存:

1. 定义Activity级ViewModel

创建与Activity生命周期绑定的ViewModel,确保数据在Fragment切换、配置变化时不丢失:

class HomeViewModel(application: Application) : AndroidViewModel(application) {
    private val repository = HomeRepository(application)
    // 暴露Room数据流供Fragment观察
    val homeItems: LiveData<List<HomeItem>> = repository.getPreloadedItems()

    init {
        // 初始化时触发预加载逻辑
        repository.preloadData()
    }
}

2. Room层实现预加载与缓存

在Repository中处理Room操作,预加载逻辑可在ViewModel初始化时执行,提前将数据存入本地:

class HomeRepository(private val application: Application) {
    private val homeDao = AppDatabase.getInstance(application).homeDao()

    fun getPreloadedItems(): LiveData<List<HomeItem>> {
        return homeDao.getAllItems()
    }

    fun preloadData() {
        viewModelScope.launch(Dispatchers.IO) {
            // 示例:若Room无数据,插入预加载内容
            if (homeDao.getItemCount() == 0) {
                val preloadedList = listOf(HomeItem("Item 1"), HomeItem("Item 2"))
                homeDao.insertAll(preloadedList)
            }
        }
    }
}

3. Fragment绑定ViewModel与RecyclerView

Fragment中获取ViewModel并观察数据,由于Fragment未被销毁,RecyclerView的适配器与数据会直接保留,切换回来可瞬间显示:

class HomeFragment : Fragment() {
    private lateinit var viewModel: HomeViewModel
    private lateinit var recyclerView: RecyclerView
    private lateinit var adapter: HomeAdapter

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view = inflater.inflate(R.layout.fragment_home, container, false)
        recyclerView = view.findViewById(R.id.recyclerView)
        adapter = HomeAdapter()
        recyclerView.adapter = adapter
        recyclerView.layoutManager = LinearLayoutManager(context)
        return view
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // 获取Activity级ViewModel,确保数据持久化
        viewModel = ViewModelProvider(requireActivity())[HomeViewModel::class.java]
        viewModel.homeItems.observe(viewLifecycleOwner) { items ->
            adapter.submitList(items)
        }
    }
}

三、额外优化:保留RecyclerView滚动位置

若需应对配置变化(如屏幕旋转)导致的滚动位置丢失,可通过LayoutManager的状态保存与恢复实现:

private var layoutManagerState: Parcelable? = null

override fun onPause() {
    super.onPause()
    layoutManagerState = recyclerView.layoutManager?.onSaveInstanceState()
}

override fun onResume() {
    super.onResume()
    layoutManagerState?.let {
        recyclerView.layoutManager?.onRestoreInstanceState(it)
    }
}

注意事项

  • app:keepAlive="true"仅支持Jetpack Navigation 2.4.0及以上版本,需确保依赖版本达标。
  • 优先使用Activity级ViewModel,避免Fragment级ViewModel因意外销毁导致数据丢失。
  • Room操作必须在后台线程执行,借助viewModelScope处理异步逻辑,避免阻塞主线程。

内容的提问来源于stack exchange,提问作者TenolToSy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 14:35:09