我们浏览以下模块:使用Jetpack在多模块应用程序中导航


几乎每个成长中的项目迟早都会开始关注多模块架构。 当仅更改一项功能时,开发人员不希望等到整个项目重新组合后再进行。 多模块性有助于相互隔离应用程序功能,从而减少构建时间。 但是,这种隔离对组件的范围施加了一些限制。 当我们在一个模块的项目中使用从Jetpack进行导航时,可以从任何应用程序包中获得导航图,我们始终可以明确指定NavController应该执行的操作,并且如果项目中存在嵌套片段,也可以访问全局主机。 但是,当有许多模块时,就会出现问题:在何处构建导航图,如何访问它以及如何不混淆模块的依赖关系。 我们将讨论所有这些削减。


导航图


设计多模块应用程序时要记住的最重要的事情是依赖性。 模块依赖关系树中的依赖关系应指向一个方向。



多模块应用程序中最依赖的模块始终是应用程序模块。 他几乎了解其他所有模块。 在应用程序中,DI通常使用各种框架来实现。 使用app模块的这种依赖关系,您可以在其中实现主要主机的导航图。


您应该始终记住,应用程序模块应该实现尽可能少的功能,因为它是最依赖的,项目中几乎任何更改都会导致应用程序模块的重新组装。


谈话很便宜。 给我看代码


并立即给出一个真实的例子。 我们的情况是:应用程序的入口是启动屏幕,它确定要转到的屏幕:主要功能或授权。 在授权屏幕上,仅过渡到主要功能。 和往常一样,我们构建一个导航图-没什么复杂的。



访问模块内部的导航


当需要从一个屏幕过渡到另一个模块的屏幕时,就会出现一个问题-如何?


实际上,在功能模块内部,无法访问导航图来获取NavController必须执行的动作ID。


这可以通过使用接口实现DI来解决。 我们将创建一个接口并将其命名为WhatToNavCommandProvider,而不是依赖于来自应用程序模块的全局导航图的功能模块,该接口的变量是导航命令。


SplashNavCommandProvider.kt


interface SplashNavCommandProvider { val toAuth: NavCommand val toMain: NavCommand } 

命令提供者的接口将在app模块中实现,导航命令的类将与NavController.navigate方法的参数具有相同的字段。


导航命令


 data class NavCommand( val action: Int, var args: Bundle? = null, val navOptions: NavOptions? = null ) 

让我们看看它在实际中的外观。 从初始屏幕可以进行两种转换:到授权屏幕和主功能屏幕。 在启动模块中,创建一个接口:


SplashNavCommandProvider.kt


 interface SplashNavCommandProvider { val toAuth: NavCommand val toMain: NavCommand } 

在app模块中,我们创建此接口的实现,并使用di框架(我有Dagger),通过初始接口将其提供给模块。


SplashNavCommandProviderImpl.kt-CommandProvider的实现


 class SplashNavCommandProviderImpl @Inject constructor() : SplashNavCommandProvider { override val toAuth: NavCommand = NavCommand(R.id.action_splashFragment_to_authFragment) override val toMain: NavCommand = NavCommand(R.id.action_splashFragment_to_mainFragment) } 

SplashNavigationModule.kt-用于提供依赖项的DI模块


 @Module interface SplashNavigationModule { @Binds fun bindSplashNavigator(impl: SplashNavCommandProviderImpl): SplashNavCommandProvider } 

AppActivityModule.kt-应用程序的主要DI模块


 @Module interface AppActivityModule { @FragmentScope @ContributesAndroidInjector( modules = [ SplashNavigationModule::class ] ) fun splashFragmentInjector(): SplashFragment … } 

在启动模块中,我们在MV中(此处)Presenter或ViewModel中实现实现。


SplashViewModel.kt


 class SplashViewModel @Inject constructor( private val splashNavCommandProvider: SplashNavCommandProvider ) ... 

当屏幕的逻辑认为是时候切换到另一个屏幕时,我们将命令传输到片段,并通知我们需要切换到另一个屏幕。


可以直接在片段中实现SplashNavCommandProvider的实现,但随后我们将失去测试导航的能力。


在片段本身中,要完成过渡,您需要获取NavController。 如果当前屏幕不是嵌套片段,那么我们只需使用findNavController()方法获取NavController并在其上调用navigation方法:


 findNavController().navigate(toMain) 

您可以通过编写该片段的扩展名使其更加方便


Framentmentxt.kt


 fun Fragment.navigate(navCommand: NavCommand) { findNavController().navigate(navCommand.action, navCommand.args, navCommand.navOptions) } 

为什么只为碎片? 因为我使用SingleActivity方法,所以如果您有多个方法,则还可以为Activity创建扩展。


然后片段中的导航将如下所示


 navigate(toMain) 

嵌套片段


嵌套片段中的导航可以有两种类型:


  • 嵌套容器中的过渡
  • 进入容器一个或多个级别。 例如,全球活动主持人

在第一种情况下,一切都很简单,我们上面编写的扩展名适合我们。 要完成第二种情况的转换,您需要获取所需主机的NavController。 为此,您需要在模块内部获取此主机的ID。 由于只有实现该主机导航图的模块才可以访问它,因此我们将创建一个依赖项,并将其实现在功能模块中,在该功能模块中,需要通过Dagger访问特定的NavController。


GlobalHostModule.kt-用于提供全局主机ID依赖关系的DI模块


 @Provides @GlobalHost fun provideGlobalHostId(): Int = R.id.host_global 

AppActivityModule.kt-应用程序的主要DI模块


 @FragmentScope @ContributesAndroidInjector( modules = [ GlobalHostModule::class, ProfileNavigationModule::class, ... ] ) fun profileKnownFragmentInjector(): ProfileKnownFragment 

将主机ID依赖项嵌入片段中


 @Inject @GlobalHost var hostId = 0 

嵌套片段时,值得为每个主机创建限定符,也可以使用现有的限定符Name来使Dagger理解要提供的整数。


Globalhost.kt


 @Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class GlobalHost 

在片段中获得所需主机的依赖项ID之后,您可以通过主机ID获取NavController。 我们将改进扩展能力,以便在任何容器中进行转换:


Framentmentxt.kt


 fun Fragment.navigate(navCommand: NavCommand, hostId: Int? = null) { val navController = if (hostId == null) { findNavController() } else { Navigation.findNavController(requireActivity(), hostId) } navController.navigate(navCommand.action, navCommand.args, navCommand.navOptions) } 

代码段中的代码


 navigate(toAuth, hostId) 

这些是在多模块体系结构中使用Jetpack进行导航的亮点。 如果您有任何问题,我将很乐意在评论中回答:)

Source: https://habr.com/ru/post/zh-CN475104/


All Articles