探索Android上的动态功能导航

Since the introduction of the Navigation Component on Android, navigating the different parts of our application has become much more pleasant to implement. We’ve been able to better decouple navigation logic from our activities and fragments, along with being able to test these paths with more ease. However, the Navigation Component has only ever allowed us to achieve these things with components contained within Android application or library modules — with these not being the only kind of modules that our Android projects support, developers have been eager for more module type inclusion for the navigation component. For example, when it comes to the use of Dynamic Feature Modules within our applications, these cannot be navigated to via the use of the Navigation Component. For these cases, the new Dynamic Feature Navigation library extends from the Navigation Component to allow us to perform navigation which involves destinations defined within Dynamic Feature Modules.

自从在Android上引入导航组件以来,导航应用程序的不同部分变得更加容易实现。 我们已经能够更好地将导航逻辑与活动和片段分离开来,并且能够更轻松地测试这些路径。 但是,导航组件只允许我们使用Android应用程序或库模块中包含的组件来实现这些目的-由于这些并不是我们的Android项目支持的唯一模块,因此开发人员一直渴望将更多的模块类型包含在其中。导航组件。 例如,在我们的应用程序中使用动态功能模块时 ,不能通过使用导航组件将其导航到。 对于这些情况,新的动态要素导航库从导航组件扩展而来,使我们能够执行涉及动态要素模块中定义的目的地的导航。

Whilst it’s incredibly helpful being able to perform dynamic feature navigation with the navigation library, there is one big concern that might arise — what if the dynamic feature isn’t actually installed on the device at the time of navigation? Luckily for us, the new dynamic navigator library provides some classes which will help us out in this side of things. Because these modules can either come with the initial app download or be installed when requested, we can’t just navigate to these components in the same way that we would handle other parts of our application. Aside from the dynamic feature not being installed at the time of navigation — what if the feature is still downloading, or fails to download before navigating to? When it comes to these cases, there are a lot of different states to think about. Luckily for us, the new Dynamic Feature Navigation Component aims to ease this process, not only handling the navigation to dynamic features, but also handling the different states of install which dynamic features may be in.

能够通过导航库执行动态功能导航非常有用,但可能会引起一个大问题-如果在导航时未在设备上实际安装动态功能,该怎么办? 对我们来说幸运的是,新的动态导航器库提供了一些类,可以在这方面帮助我们。 由于这些模块可以随最初的应用程序下载一起提供,也可以根据要求进行安装,因此我们不能仅以与处理应用程序其他部分相同的方式导航至这些组件。 除了在导航时未安装动态功能之外,如果该功能仍在下载中,或者在导航至该功能之前未下载怎么办? 当涉及到这些情况时,有许多不同的状态需要考虑。 对我们来说幸运的是,新的动态功能导航组件旨在简化此过程,不仅处理动态功能的导航,而且还处理动态功能可能处于的不同安装状态。

In this post we’re going to dive into the Dynamic Feature Navigation library, learning not only about how we can utilise it within our applications, but also how its components work under the hood.

在本文中,我们将深入研究动态功能导航库,不仅学习如何在应用程序中利用它,而且还了解其组件在后台的工作方式。

Note: Some of the concepts in this article will reference the Navigation Component. If you are unfamiliar with any concepts mentioned, it would be worth checking out the guides/documentation for the Navigation Component.

注意:本文中的某些概念将引用导航组件。 如果您不熟悉所提到的任何概念,那么有必要检查一下导航组件指南/文档

This was originally posted on joebirch.co

这最初发布在 joebirch.co

导航到动态功能目标 (Navigating to a Dynamic Feature destination)

Before we get started with the library, we need to go ahead and add the dependency to our application. It’s important to note that the library is still in alpha — so if you do get a chance to play with it then it’s a great time to provide feedback to the developers working on it.

在开始使用该库之前,我们需要继续并将依赖项添加到我们的应用程序中。 需要特别注意的是,该库仍处于Alpha状态,因此,如果您有机会使用它,那么现在正是向开发该库的开发人员提供反馈的好时机。

implementation "androidx.navigation:navigation-dynamic-features-
fragment:2.3.0-alpha03"

If we were previously using the Navigation Component library, we would have had something that looked like this for our host fragment:

如果我们以前使用的是Navigation Component库,那么对于我们的宿主片段,我们将会有类似以下内容的东西:

<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"
app:navGraph="@navigation/main_nav" />

When it comes to the Dynamic Navigator, we need to switch this out for the new DynamicNavHostFragment. This navigation host class allows us to handle navigation using destinations that are defined within dynamic feature modules.

对于动态导航器,我们需要将其切换为新的DynamicNavHostFragment。 该导航主机类使我们可以使用在动态要素模块中定义的目标来处理导航。

<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment" android:name="androidx.navigation.dynamicfeatures .fragment.DynamicNavHostFragment"
app:defaultNavHost="true"
app:navGraph="@navigation/main_nav" />

Now that our navigation host is using the DynamicNavHostFragment class, we can go ahead and add our first navigation destination from our feature module into our graph.

现在,我们的导航主机正在使用DynamicNavHostFragment类,我们可以继续并将功能模块中的第一个导航目标添加到图形中。

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_nav"
app:startDestination="@id/mainFragment">
<fragment
android:id="@+id/mainFragment"
android:name="co.joebirch.navigationsample.MainFragment" >
</navigation>

At this point we have our navigation graph, along with our startDestination defined as our mainFragment reference. With this declared we now want to go ahead and add a destination to our graph – our first destination is going to be from a dynamic feature module. Let’s go ahead and add this to our navigation graph.

在这一点上,我们有我们的导航图,以及我们startDestination定义为我们的mainFragment参考。 声明了这个之后,我们现在要继续向图添加一个目标–我们的第一个目标将来自动态要素模块。 让我们继续将其添加到导航图。

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_nav"
app:startDestination="@id/mainFragment">
<fragment
android:id="@+id/mainFragment"
android:name="co.joebirch.navigationsample.MainFragment" />
<fragment
app:moduleName="feature_one"
android:id="@+id/featureOneFragment"
android:name="co.joebirch.feature_one.FeatureOneFragment" />
</navigation>

We’ve added our destination here with the id of featureOneFragment. You’ll notice here that we have three different properties that we’ve set on our fragment destination:

我们在此处添加了具有featureOneFragment id的目的地 。 您会在这里注意到,我们在片段目标上设置了三个不同的属性:

  • moduleName – this is the name of the module that our navigation destination resides in. The library will look inside of this module when locating the destination, acting as the glue between the navigation graph and the specific destination. This is the key addition when compared to application / library module navigation.

    moduleName –这是我们的导航目标所在的模块的名称。在定位目标时,库将在此模块内部查找,充当导航图和特定目标之间的黏合剂。 与应用程序/库模块导航相比,这是关键的补充。

  • id – the id of the navigation destination

    id –导航目标的id

  • name – the name of the fragment used for this navigation destination

    name –用于此导航目标的片段的名称

With these defined our graph now has sufficient information to locate the fragment being used as our destination – now we need to actually configure the navigation to it. When it comes to this, the approach will look very similar to how we may have configured the Navigation Component for application and library module navigation.

有了这些定义,我们的图现在就具有足够的信息来定位用作目标的片段–现在,我们需要实际配置对其的导航。 当涉及到这一点时,该方法将与我们为应用程序和库模块导航配置导航组件的方式非常相似。

To begin with we’ll go ahead and add an action to our mainFragment so that we can navigate to our feature module destination:

首先,我们将继续,并向mainFragment添加一个动作,以便我们可以导航到功能模块的目标位置:

<fragment
android:id="@+id/mainFragment"
android:name="co.joebirch.navigationsample.MainFragment" >
<action
android:id="
@+id/action_mainFragment_to_featureOneFragment"
app:destination="@id/featureOneFragment" />
</fragment>

From our code we can now use a Nav Controller reference to trigger this action and perform the navigation that is defined by it:

现在,从我们的代码中,我们可以使用Nav Controller引用来触发此操作并执行其定义的导航:

findNavController().navigate(
R.id.action_mainFragment_to_featureOneFragment)

包括来自动态特征模块的图形 (Including graphs from Dynamic Feature modules)

At this point we’ve added navigation for a dynamic feature module using our navigation graph, but in some cases we may have slightly more complex navigation to configure. For example, we may have a dynamic feature module which defines its own navigation graph – this would need to be referenced from our navigation graph that we defined above. Let’s say we have the following navigation graph defined within a second dynamic feature module:

至此,我们已经使用导航图为动态功能模块添加了导航,但是在某些情况下,我们可能需要配置稍微复杂一些的导航。 例如,我们可能有一个动态要素模块,该模块定义了自己的导航图–需要从上面定义的导航图中引用。 假设我们在第二个动态要素模块中定义了以下导航图:

<navigation    
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:startDestination="@id/secondFeatureFragmentOne">
<fragment
android:id="@+id/secondFeatureFragmentOne"
android:name="co.joebirch.navigationsample.
feature_two.FeatureTwoFragmentOne">
<action
android:id="@+id/action_secondFeatureFragmentOne
_to_secondFeatureFragmentTwo"
app:destination="@id/secondFeatureFragmentTwo" />
</fragment>
<fragment
android:id="@+id/secondFeatureFragmentTwo"
android:name="co.joebirch.navigationsample.
feature_two.FeatureTwoFragmentTwo" />
</navigation>

Within this dynamic feature module, let’s say we have it defining its own navigation graph – regardless of whether or not the graph is more complex than this one, this helps to keep the responsibility of this contained. Now, we’re going to want to include this as part of our global navigation graph that we initially defined so that we can navigate to the fragments defined in this new graph. If we head back over to our main_nav graph then we can add the following information:

假设在这个动态功能模块中,我们定义了自己的导航图–不管该图是否比该图更复杂,这都有助于保持其责任。 现在,我们希望将其作为最初定义的全局导航图的一部分,以便可以导航到此新图中定义的片段。 如果我们回到main_nav图,则可以添加以下信息:

<include-dynamic
android:id="@+id/featureNav"
app:moduleName="secondFeature"
app:graphResName="second_feature_nav"
app:graphPackage="co.joebirch.navigationsample.feature_two" />

The include tag is already available in the navigation library, allowing us to add navigation graphs from standard library modules in our project. The Dynamic Feature Navigator library adds this new include-dynamic tag which can be used to add references to navigation graphs from within dynamic feature modules. This tag has four attributes which can be defined:

include标签已经在导航库中可用,允许我们从项目中的标准库模块添加导航图。 动态功能导航器库添加了这个新的include-dynamic标记,该标记可用于从动态功能模块中添加对导航图的引用。 该标签具有四个可以定义的属性:

  • id – the id of the navigation graph

    id –导航图的ID

  • moduleName – the name of the module that the graph is located in

    moduleName –图形所在的模块的名称

  • graphResName – the resource identifier used for the graph

    graphResName –用于图的资源标识符

  • graphPackage – the package where the graph file is located

    graphPackage –图形文件所在的包

With this include-dynamic tag added to our original main_nav graph we can now perform navigation actions on it. Let’s replace the action_mainFragment_to_featureOneFragment action within our graph so that we can navigate to the graph contained within our include-dynamic tag.

通过将此include-dynamic标记添加到原始main_nav图中,我们现在可以对其执行导航操作。 让我们替换图形中的action_mainFragment_to_featureOneFragment动作,以便我们可以导航到包含include-dynamic标记中的图形。

<action
android:id="@+id/action_mainFragment_to_featureNav"
app:destination="@id/featureNav" />

With this in place, when we trigger the action_mainFragment_to_featureNav action from our nav controller, the startDestination from our featureNav graph will be displayed and any following navigational behaviour will be taken from that graph.

设置好此位置后,当我们从导航控制器触发action_mainFragment_to_featureNav操作时,将显示来自featureNav图的startDestination,并将从该图获取任何后续的导航行为。

在导航过程中处理动态功能安装 (Handling Dynamic Feature install during navigation)

Whilst it’s incredibly helpful being able to perform dynamic feature navigation with the navigation library, there is one big concern that might arise – what if the dynamic feature isn’t actually installed on the device at the time of navigation? Luckily for us, the Dynamic Feature Navigation library provides some classes which will help us out in this side of things.

能够通过导航库执行动态功能导航非常有用,但可能会引起一个大问题-如果在导航时未在设备上实际安装动态功能,该怎么办? 对我们来说幸运的是,动态要素导航库提供了一些类,可以在这方面帮助我们。

When navigation is performed to a dynamic feature module, as defined by the module attribute for the destination in our graph, the library will first check if the feature is installed on the device. If installed, navigation will be performed to the destination. Otherwise, a progress fragment will be displayed whilst the dynamic feature will be installed and the user will be navigated to the destination once installation has completed.

当导航到动态功能模块时(如图形中目标的模块属性所定义),库将首先检查功能是否已安装在设备上。 如果已安装,将导航到目的地。 否则,将在安装动态功能的同时显示进度片段,并在安装完成后将用户导航到目标位置。

Image for post

When it comes to this progress fragment that is displayed during installation, it will handle the whole installation process for us – this includes any loading, success or error states that occur. This progress fragment is in the form of the DefaultProgressFragment class – which extends from the AbstractProgressFragment class. Whilst this default fragment handles everything for us, we might want to provide our own customised implementation to be used during the install process. However, it is strongly recommended to use the DefaultProgressFragment unless you need to add extended functionality for this piece of the flow or customize the progress UI beyond the defaults.

当涉及到安装过程中显示的进度片段时,它将为我们处理整个安装过程-包括发生的任何加载,成功或错误状态。 此进度片段采用DefaultProgressFragment类的形式–从AbstractProgressFragment类扩展。 尽管此默认片段可以为我们处理所有事情,但我们可能希望提供我们自己的自定义实现,以在安装过程中使用。 但是,强烈建议使用DefaultProgressFragment,除非您需要为此流程添加扩展功能或自定义默认值以外的进度UI。

When it comes to providing our own progress fragment, it needs to extend the AbstractProgressFragment class which means we will be required to implement the following methods:

在提供我们自己的进度片段时,需要扩展AbstractProgressFragment类,这意味着我们将需要实现以下方法:

  • onCancelled() — Called when the user cancels the installation process

    onCancelled() —在用户取消安装过程时调用

  • onFailed(errorCode: Int) — Called when the installation of the dynamic feature has failed, with the error indicated by the provided errorCode

    onFailed(errorCode:Int) —当动态功能安装失败时调用,错误由提供的errorCode指示

  • onProgress(status: Int, bytesDownloaded: Long, bytesTotal: Long) — Called whenever there is a progress update regarding the installation of the dynamic feature. Here, the bytesDownloaded represents the number of bytes that have been downloaded so far, along with bytesTotal that is the total number of bytes that need to be downloaded. Finally, the status will be one of the SplitInstallSessionStatus values which can be used to determine the current status of the dynamic feature install.

    onProgress(状态:整数,字节下载数:长,字节总数:长) —每当关于动态功能安装的进度更新时调用。 在这里,bytesDownloaded表示到目前为止已下载的字节数,以及bytesTotal,这是需要下载的字节总数。 最后,状态将是SplitInstallSessionStatus值之一,可用于确定动态功能部件安装的当前状态。

Once we have our customised progress fragment, we can set it using the app:progressDestination attribute to the destination ID which is handling our installation progress.

有了自定义进度片段后,就可以使用app:progressDestination属性将其设置为正在处理安装进度的目标ID。

Image for post

监视动态功能安装 (Monitoring dynamic feature install)

In some cases we may wish to implement a non-blocking installation flow for our dynamic feature — for example, rather than showing some form of the AbstractProgressFragment we may wish to keep the user in the current context that they are in. This approach can help to achieve a smoother experience for the user and remove any blocking experience that may come from a progress screen.

在某些情况下,我们可能希望为我们的动态功能实现无阻碍的安装流程-例如,我们不希望显示某种形式的AbstractProgressFragment,而是希望将用户保持在他们所处的当前上下文中。这种方法可以帮助您为用户提供更流畅的体验,并消除进度屏幕中可能出现的任何阻塞体验。

Within the AbstractProgressFragment class there are some internals which are monitoring the install status of the dynamic feature, relaying this information back to our fragment implementation via the onProgress() override. This is handled using the DynamicInstallMonitor class, which is actually available for us to use outside of this progress fragment — meaning that we can allow the user to trigger an install from navigation, monitor the progress state whilst they continue their current tasks and navigate them once the install has completed (handling any other states along the way, such as errors).

在AbstractProgressFragment类中,有一些内部组件正在监视动态功能的安装状态,并将这些信息通过onProgress()重写传递回我们的片段实现。 这是使用DynamicInstallMonitor类处理的,该类实际上可供我们在此进度片段之外使用-这意味着我们可以允许用户从导航触发安装,监视进度状态,同时继续执行当前任务并浏览一次安装已完成(处理过程中的其他任何状态,例如错误)。

Image for post

We can begin here by creating a new reference to this monitor:

我们可以从这里开始创建对此监视器的新引用:

val installMonitor = DynamicInstallMonitor()

Before we begin any monitoring, we’re first going to perform our navigation. When doing so, we need to pass in an instance of the DynamicExtras class. This class essentially acts as a container for us to pass attributes when handling navigation for dynamic features. To build an instance of this class we can use the corresponding Builder to do so:

在开始任何监视之前,我们首先要执行导航。 这样做时,我们需要传入DynamicExtras类的实例。 在处理动态功能的导航时,此类实际上充当我们传递属性的容器。 要构建此类的实例,我们可以使用相应的Builder来进行:

val dynamicExtras = DynamicExtras.Builder()
.setInstallMonitor(installMonitor)
.build()

The builder currently allows us to set two different references:

该构建器当前允许我们设置两个不同的引用:

  • installMonitor — The reference used to monitor the current install state of the dynamic feature

    installMonitor —用于监视动态功能部件的当前安装状态的参考

  • destinationExtras — Any Navigator.Extras that we wish to pass for navigation

    destinationExtras —我们希望传递给导航的任何Navigator.Extra

Now that we have a reference to our DynamicExtras we can go ahead and perform the navigation on our navigation controller:

现在,我们已经参考了DynamicExtras,我们可以继续在导航控制器上执行导航了:

findNavController().navigate(
destinationId,
null,
null,
dynamicExtras
)

Once we’ve triggered this navigate operation we need to immediately check the installed status for our dynamic feature. The install monitor instance that we previously instantiated allows us to check this using its isInstallRequired field, this will return either:

触发此导航操作后,我们需要立即检查动态功能的安装状态。 我们先前实例化的安装监视器实例允许我们使用其isInstallRequired字段对其进行检查,这将返回以下之一:

  • false — meaning that install is not required and we can continue to perform the navigation

    false-表示不需要安装,我们可以继续执行导航

  • true — install of the dynamic feature is required, meaning that we’ll need to observe the install state and perform the navigation once the install has completed.

    true-需要安装动态功能,这意味着我们需要观察安装状态并在安装完成后执行导航。

At this point, if installation is required then we need to observe the install state. Whilst the installation process happens automatically, we check for this install state so that we know whether or not to observe the install status of the dynamic feature. Our installMonitor reference exposes a LiveData<SplitInstallSessionState> instance that allows us to observe when the state of the install changes. We can then use the value of this state to depict what should be shown within our UI. Finally, once the state represents a terminal state we need to remove our observer as observation is no longer required.

此时,如果需要安装,那么我们需要观察安装状态。 虽然安装过程会自动进行,但我们会检查此安装状态,以便我们知道是否观察动态功能的安装状态。 我们的installMonitor引用公开了一个LiveData <SplitInstallSessionState>实例,该实例使我们能够观察安装状态何时更改。 然后,我们可以使用此状态的值来描述应该在UI中显示的内容。 最后,一旦状态代表最终状态,我们便需要删除观察者,因为不再需要观察。

installMonitor.status.observe(viewLifecycleOwner, 
Observer { state ->
when (state.status()) {
SplitInstallSessionStatus.INSTALLED -> { }
SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION -> {
// Larger feature downloads require user confirmation
splitInstallManager.startConfirmationDialogForResult(
state,
this,
REQUEST_CODE_INSTALL_CONFIRMATION
)
}
SplitInstallSessionStatus.FAILED -> {}
SplitInstallSessionStatus.CANCELED -> {}
...
} if (state.hasTerminalStatus()) {
installMonitor.status.removeObservers(viewLifecycleOwner)
}
})

We can see above that there are a collection of states which the flow could be in at any time, whilst it depends on the UI that you are planning to show here, the UX guide for dynamic delivery will help to handle each of the above states.

上面我们可以看到,流随时可能处于一组状态,这取决于您打算在此处显示的UI, 动态交付UX指南将有助于处理上述每个状态。

引擎盖下 (Under the hood)

Now that we know how we can handle dynamic feature navigation in our apps, I want to take a little look at how things are working under the hood.

既然我们知道了如何在应用程序中处理动态功能导航,那么我想看一下事情的幕后运作方式。

In the sections above we touched on this DynamicNavHostFragment class — this new class actually extends from the NavHostFragment that is used for non-dynamic feature navigation. The DynamicNavHostFragment does this in order to override the onCreateNavController() function of the NavHostFragment, using a collection of new classes within this to provide the functionality of dynamic navigation.

在上面的部分中,我们介绍了此DynamicNavHostFragment类-这个新类实际上是从用于非动态要素导航的NavHostFragment扩展的。 DynamicNavHostFragment这样做是为了覆盖NavHostFragment的onCreateNavController()函数,使用其中的一组新类来提供动态导航的功能。

Image for post

If we start at the top we have the NavHostFragment class — this is already available in the current navigation library, so I don’t want to go too much into this. If you’re not familiar with it already though, this class is used to act as a container for the content displayed within our navigation graph. If you noticed in the diagram above, the DynamicNavHostFragment is a new class within the Dynamic Navigation library that acts as the Nav Host for dynamic features — this class extends the original NavHostFragment, so a lot of the functionalities are inherited from that base class. The new Host Fragment class actually only overrides a single method from the base class, the onCreateNavController method. This override is used to configure some extra parts of the navigation logic during initialisation.

如果我们从顶部开始,我们将拥有NavHostFragment类-当前导航库中已经提供了该类,因此我不想对此做过多介绍。 如果您现在还不熟悉它,则可以使用该类作为导航图内显示内容的容器。 如果您在上图中注意到,DynamicNavHostFragment是Dynamic Navigation库中的一个新类,它充当动态功能的Nav Host —该类扩展了原始NavHostFragment,因此许多功能都从该基类继承。 实际上,新的Host Fragment类仅覆盖基类中的单个方法onCreateNavController方法。 此覆盖用于在初始化期间配置导航逻辑的一些额外部分。

Within this onCreateNavController method we can see that there are a collection of new navigation handling classes being configured. During this onCreateNavController the NavigatorProvider for the provided navigation controller is populated with the additional providers which are required for dynamic navigation.

在此onCreateNavController方法中,我们可以看到有一组正在配置的新导航处理类。 在此onCreateNavController期间,将为提供的导航控制器的NavigatorProvider填充动态导航所需的其他提供程序。

override fun onCreateNavController(navController: NavController) {
super.onCreateNavController(navController) ...
val navigatorProvider = navController.navigatorProvider
navigatorProvider += DynamicActivityNavigator(
requireActivity(), installManager) val fragmentNavigator = DynamicFragmentNavigator(
requireContext(), childFragmentManager, id, installManager)
navigatorProvider += fragmentNavigator val graphNavigator = DynamicGraphNavigator(
navigatorProvider,
installManager
)
...
navigatorProvider += graphNavigator
navigatorProvider += DynamicIncludeGraphNavigator(
requireContext(), navigatorProvider,
navController.navInflater, installManager)
}

If we look at the default navigation component, we can see that there are a collection of classes which extend from a Navigator abstract class — this class is used to define a mechanism for navigating within an app. The classes which extend from this are required to implement the defined methods in order to build the navigation graph for the defined components. When it comes to dynamic feature navigation, these original classes have been reused to aid the implementation of dynamic navigation. In fact, each component navigator extends from its corresponding navigator class, overriding the navigate() function to handle the new requirements for dynamic feature navigation.

如果我们查看默认的导航组件,则可以看到有一个从Navigator抽象类扩展的类的集合-该类用于定义在应用程序内进行导航的机制。 从此扩展的类是实现定义的方法所必需的,以便为定义的组件构建导航图。 当涉及动态要素导航时,这些原始类已被重用以帮助实现动态导航。 实际上,每个组件导航器都从其对应的导航器类扩展而来,它覆盖了navigation()函数,以处理动态要素导航的新要求。

Image for post

For example, we can jump into the DynamicActivityNavigator class and see the difference in the way it is handling navigation via the use of the DynamicInstallManager:

例如,我们可以跳入DynamicActivityNavigator类,并通过使用DynamicInstallManager来查看其处理导航方式的差异:

override fun navigate(
destination: ActivityNavigator.Destination,
args: Bundle?,
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?
): NavDestination? {
val extras = navigatorExtras as? DynamicExtras
if (destination is Destination) {
val moduleName = destination.moduleName
if (moduleName != null &&
installManager.needsInstall(moduleName)) {
return installManager.performInstall(
destination, args, extras, moduleName)
}
}
return super.navigate(
destination,
args,
navOptions,
if (extras != null) {
extras.destinationExtras
} else {
navigatorExtras
}
)
}

Pretty similar to how we previously saw things in this article, right? The same flow of checking if the feature is available and then handling the navigation based off of that state. Without this new navigator class, the navigation component would be unable to handle these different states for dynamic features.

与我们之前在本文中看到的情况非常相似,对吧? 检查功能是否可用的相同流程,然后根据该状态处理导航。 没有这个新的导航器类,导航组件将无法处理动态功能的这些不同状态。

From the Navigation Component you may recall the concept of a NavDestination, this class is used to represent a single node within a navigation graph — the nodes (destinations) are then pieced together to create the graph which represents an applications navigational flow. Each of the Navigator classes in the navigation component housed their own implementation of the NavDestination. When it comes to dynamic features and their navigators, exactly the same applies.

在导航组件中,您可能还记得NavDestination的概念,该类用于表示导航图中的单个节点-然后将节点(目的地)拼凑在一起,以创建表示应用程序导航流程的图。 导航组件中的每个Navigator类都具有自己的NavDestination实现。 对于动态功能及其导航器,完全相同。

Image for post

If we again use the DynamicActivityNavigator as an example we can see that the Destination for this class extends from the initial ActivityNavigator.Destination class. This definition of the destination is instead used to house the module name that holds the dynamic feature, something that is not supported by the initial implementation. This can then be used during the navigate() method in our DynamicActivityNavigator class when it comes to navigating to this activity.

如果再次使用DynamicActivityNavigator作为示例,则可以看到该类的Destination从初始ActivityNavigator.Destination类扩展。 相反,此目标定义用于容纳包含动态功能的模块名称,而初始实现不支持该名称。 然后,当涉及到导航到此活动时,可以在我们的DynamicActivityNavigator类中的navigation()方法期间使用此方法。

class Destination : ActivityNavigator.Destination {
var moduleName: String? = null constructor(navigatorProvider: NavigatorProvider) :
super(navigatorProvider)constructor(
activityNavigator:
Navigator<out ActivityNavigator.Destination>
) : super(activityNavigator) override fun onInflate(context: Context, attrs: AttributeSet) {
super.onInflate(context, attrs)
context.withStyledAttributes(attrs,
R.styleable.DynamicActivityNavigator) {
moduleName =
getString(
R.styleable.DynamicActivityNavigator_moduleName)
}
}
}

These Destination classes are then used in the same ways as previously done so by the navigation component. Here, dynamic feature navigation is building off of what already exists to extend the functionality for dynamic navigation.

然后,以与导航组件先前相同的方式使用这些Destination类。 在这里,动态功能导航是基于已经存在的功能来扩展动态导航功能的。

As we can see from the small dive into the source of dynamic feature navigation, a lot of what already existed for the Navigation Component has been built off of to add support for navigation via dynamic features. This allows developers to hook into navigational approaches that we are already familiar with and promoting reuse not only within the source, but within our projects also.

从对动态功能导航源的一小部分中可以看出,导航组件的许多现有功能已经建立,以增加对通过动态功能进行导航的支持。 这使开发人员可以使用我们已经熟悉的导航方法,并不仅在源内而且在我们的项目内促进重用。

— -

--

Throughout this post we’ve learnt how we can use the Dynamic Feature Navigation library to perform navigation for our feature models — regardless of their install state on our users device. Using this library allows us to implement frictionless navigation flows, offsetting most of the hard work onto this library, allowing us to focus on building great apps. Whilst we didn’t dive too deep into how this works under the hood, we’ve seen a high level view of how things are operating and the reuse of code from the original navigation component.

在整个本文中,我们学习了如何使用动态功能导航库对功能模型进行导航-不管它们在用户设备上的安装状态如何。 使用该库可以使我们实现无摩擦的导航流程,从而将大部分辛苦工作抵消了该库上的所有工作,从而使我们可以专注于构建出色的应用程序。 尽管我们并没有深入探讨它的工作原理,但我们已经从高层次的角度了解了事情的运行方式以及原始导航组件中代码的重用。

With all this together I look forward to seeing how you’ll be using this library for dynamic feature navigation within your applications! If you have any questions on how it can be used, or thoughts that you’d like to share, please do reach out in the comments 🙌

综上所述,我期待看到您将如何使用该库在应用程序中进行动态功能导航! 如果您对如何使用它有任何疑问,或者想分享您的想法,请在评论中提供帮助🙌

Thanks to Ben Weiss, ashdavies ™ & Wajahat Karim for reviewing and providing feedback on this post 🙌

感谢Ben Weissashdavies™Wajahat Karim审阅并提供有关此帖子的信息🙌

翻译自: https://medium.com/google-developer-experts/exploring-dynamic-feature-navigation-on-android-c803bdbbca9b

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值