如何通过Jetpack Navigation实现Fragment预加载与切换不销毁(仿YouTube)
一、阻止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
相关产品推荐
相关产品推荐

