构建单Activity的APP——通信

400 阅读3分钟

这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战

本文将介绍Navigation中的Fragment之间是如何通信的。

Fragment之间的通信

在Jetpack出来之前,Fragment之间的通信方式天花乱坠,不过一般分为俩大类:一个是在实例化Fragment的时候将数据加在目标Fragment的构造器中,然后用目标Fragment中的回调把数据再传回来;再一个就是在Activity声明一个对象,不同的Fragment对可以拿到这个对象的实例。不过官方并不建议第一种方式来实现Fragment之间的通信:

为使 Fragment 保持独立,您不应让 Fragment 直接与其他 Fragment 或与其宿主 Activity 进行通信。

新版的Fragment库提供了两个通信选项:共享的 ViewModel和 Fragment Result API。如需与任何自定义 API 共享持久性数据,应使用 ViewModel。对于包含的数据可放置在 Bundle中的一次性结果,应使用 Fragment Result API( Fragment 1.2.0 起)。

本文中将要介绍的通信方案特指应用范围更广的共享ViewModel的方案的划分。

前文中我也提到了,对于基于共享ViewModel的Fragment通信,Jetpack提供了三个范围级别的共享ViewModel,这分别是Fragment级、Navigation Graph级和Activity级,我们可以通过这三个级别覆盖APP中几乎所有的Fragment通信场景。

Fragment级

将Jetpack ViewModel引入后,想要拿到Fragment级的ViewModel只需要像下面代码所示:

class HomeFragment : Fragment() {
    override fun onCreateView(): View? {
        val homeViewModel = ViewModelProvider(this).get(HomeViewModel::class.java)
        // ...
    }
}

从上面的代码可以看出,我们首先是实例化了一个ViewModelProvider,并给它加载了一个HomeViewModel,这样一来HomeViewModel会一直在内存中,直到其范围限定到的ViewModelStoreOwner(传入的这个this,也就是HomeFragment)永久消失。所以说ViewModel实例的生命周期是超过挂载它的ViewModelStoreOwner的。

那这个级别的ViewModel对通信有什么意义呢?

这种ViewModel在父Fragment和子Fragment的通信时是比较好的选择。在我们开发平板APP的时候,经常会有一个Fragment中调用多个子Fragment的情况,只要给ViewModelProvider() 传入 requireParentFragment()就好了,这代表这俩个Fragment用的是同一个共享ViewModel。

Navigation Graph级

Navigation库可以将 ViewModel 挂载到目的地的NavBackStackEntry上,说是挂载到回退栈上,实际上可以理解为挂载在NavGraph上。

override fun onCreateView(): View {
        val factory: ViewModelProvider.Factory = ViewModelProvider.NewInstanceFactory()
        val owner: ViewModelStoreOwner = findNavController().getViewModelStoreOwner(R.navigation.mobile_navigation)
        val vm = ViewModelProvider(owner, factory)[TestViewModel::class.java]
        // ...
 }

这样NavGraph中不同的Fragment可以上相同代码实例出一个共享ViewModel。

而且由于NavGraph嵌套图的存在,可以为每个模块的嵌套图提供一个共享ViewModel,以实现嵌套图内的通信,并且这种模块在本系列后面的组件化中也适用。

Activity级

我们知道Activity的ViewModel的生命周期是长于Activity的,而且在单Activity的APP中,Activity的实例就像Application一样持久,所以Activity级别的ViewModel也可以认为是一个全局的ViewModel。

我们自然而然地会想到这种ViewModel可以当作事件总线来使用。至于如何实现事件总线那涉及到的是ViewModel和LiveData(或者Flow),之后会专门写篇文章介绍。

建议

对于单Activity的APP中的通信实现,应当遵循官方建议和编程常识,从直接跳转传参,到Fragment级,再到Activity范围的ViewModel,作用域是越来越大的,功能是越来越强的,消耗的资源也是越来越多的,在能解决通信需求的基础上,不应该滥用更大范围的通信方案。

这样不仅会造成浪费,也会导致项目越来越混乱。