【问题标题】:Multi-module Navigation with Architecture Components带有架构组件的多模块导航
【发布时间】:2020-06-08 18:21:50
【问题描述】:

所以我在当前应用程序中的模块具有这种结构。

我还没有找到任何关于多模块导航的官方文档,但是我发现了这个article,所以这是我的 gradle 文件的样子:

功能 1 - 细节

...
implementation project(":base")
implementation project(":feature-2-detail")
...

功能 2 - 细节

...
implementation project(":base")
implementation project(":feature-1-detail")
...

功能 3 - 细节

...
implementation project(":base")
implementation project(":feature-1-detail")
...

这是我的导航图:

功能 1 - 细节

<navigation ...
    android:id="@+id/graph_feature_1_id">
    <include app:graph="@navigation/graph_feature_2" />
    <fragment ...
        android:id="@+id/nav_feature_1">
        <action ...
            app:destination="@+id/graph_feature_2_id" />

    </fragment>
</navigation>

功能 2 - 细节

<navigation ...
    android:id="@+id/graph_feature_2_id">
    <include app:graph="@navigation/graph_feature_1" />
    <fragment ...
        android:id="@+id/nav_feature_2">
        <action ...
            app:destination="@+id/graph_feature_1_id" />

    </fragment>
</navigation>

功能 3 - 细节

<navigation ...
    android:id="@+id/graph_feature_3_id">
    <include app:graph="@navigation/graph_feature_1" />
    <fragment ...
        android:id="@+id/nav_feature_3">
        <action ...
            app:destination="@+id/graph_feature_1_id" />

    </fragment>
</navigation>

所以一切都适用于这种设置,但这里的问题是要将模块连接到另一个模块,我们必须将另一个功能添加为当前功能的依赖项。就像我的情况一样,Feature 1 - Detail 可以转到 Feature 2 - Detail,反之亦然,这样做会给我在 gradle 中的循环依赖。

还有其他方法可以进行多模块导航吗?我试过使用深层链接,但无济于事。

任何帮助将不胜感激!谢谢!

【问题讨论】:

  • 您是否考虑过使用深层链接进行导航?我打算开始重构功能模块设置,但我遇到了同样的问题,我唯一的解决方案是使用深度链接
  • 对于活动,我使用深度链接来执行多模块导航,但对于片段,我仍在尝试寻找解决方案。
  • 您可以通过应用模块级依赖注入提供目标片段的 id

标签: android android-architecture-components android-architecture-navigation


【解决方案1】:

这已经是一年之久,但库现在可以支持这个确切的用例!从 2.1.0-alpha03 开始,我们可以通过深层链接 URI 进行导航。

我们可以让它们彼此之间不知道并使用深层链接导航,而不是将这些功能作为实现细节添加到彼此。

功能 1 - 细节 - build.gradle

dependencies {
    implementation project(':base')
}

功能 2 - 细节相同。不需要它知道其他模块。

要进行模块间导航,我们必须首先通过deepLink 标签定义用于在该目的地导航的深层链接。

功能 1 - 详细信息 - 导航图

<navigation ...
    android:id="@+id/graph_feature_1_detail_id">
    <fragment ...
        android:id="@+id/nav_feature_1_detail">
        <deepLink app:uri="myApp://feature1detail"/>

    </fragment>
</navigation>

功能 2 - 详细信息 - 导航图

<navigation ...
    android:id="@+id/graph_feature_2_detail_id">
    <fragment ...
        android:id="@+id/nav_feature_2_detail">
        <deepLink app:uri="myApp://feature2detail"/>

    </fragment>
</navigation>

现在我们已经设置了带有 URI 的深层链接,我们可以直接在 NavController 中使用它

那么在Feature 1 - Detail 的片段中,也许是在按钮点击上?任何需要导航的地方

class Feature1DetailFragment {
   fun onViewCreated(...) {
       ...
       view.setOnClickListener {
           val uri = Uri.parse("myApp://feature2detail")
           findNavController().navigate(uri)
       }
   }
}

功能 2 - 细节中,

class Feature2DetailFragment {
   fun onViewCreated(...) {
       ...
       view.setOnClickListener {
           val uri = Uri.parse("myApp://feature1detail")
           findNavController().navigate(uri)
       }
   }
}

瞧!模块间导航。

在撰写本文时,最新的稳定版本是2.1.0-rc01

虽然我没有在更复杂的项目上尝试过这个,但我喜欢这个库,我希望看到这个库更加成熟!

我为此创建了一个Medium article。你可以看看它。干杯!

【讨论】:

  • 非常感谢您所做的一切。你的文章对我帮助很大
  • 您在返回导航方面遇到过任何问题吗?我遇到了这个问题,我从屏幕 A(应用程序模块)导航到 B(模块 B),然后导航到 C(模块 C)。当 i C 时,后退导航停止工作。但是,如果我直接从 A 导航到 C,就可以了。
  • @Kurt Acosta 如何将 Parcelable 对象从 Feature1 发送到 Feature2?
  • 嗨@Kurt,请告诉我们是否需要在 manifest 或 build.gradle 中定义这些 uri?我只是按照您的教程进行操作,但对我不起作用。
  • 我刚刚在导航图中添加了深层链接,并尝试在代码中从一个片段导航到另一个片段。但我收到以下错误。在导航图中找不到与请求 NavDeepLinkRequest{ uri=app://verily/LoginFragment } 匹配的导航目的地 NavGraph(com.app.verily:id/splash_navigation) startDestination={Destination(com.app.verily:id/splashFragment ) label=Splash class=com.app.launch.splash.SplashFragment} 请帮忙!
【解决方案2】:

可能有用的方法之一是创建一个全新的独立模块(例如“:navigation”模块)并将所有navigation.xml 文件从所有其他模块移动到它。然后我们在需要导航相关内容的所有其他模块中依赖新的 (":navigation") 模块,我们将能够访问它的 R.navigation 或生成的参数类等。

由于新的 (":navigation") 模块不知道项目中的任何其他内容,IDE 会将我们在 navigation.xml 文件中使用的任何片段、活动和其他类标记为红色,这些在其他模块外部定义但只要我们使用完整的类名(com.exampel.MyFragment),它就会编译并工作。

<?xml version="1.0" encoding="utf-8"?>
<navigation 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/nav_graph_id"
    app:startDestination="@id/some_navigation_id">

    <fragment
        android:id="@+id/some_navigation_id"
        android:name="com.exampel.MyFragment".../>
        // com.exampel.MyFragment will be marked red since IDE can't link it
        // to the existing class because it is in the other module

这为我们想要导航到的所有类创建了“隐藏”依赖项,我们需要知道类名和潜在参数,我们必须手动维护它,但它允许我们轻松地在独立模块中分离导航。

【讨论】:

  • 添加 proguard 或启用收缩后是否会破坏任何内容?
【解决方案3】:

当您在基本功能中显式声明每个功能导航图 ID 时,可以删除所有 Gradle 功能间依赖关系。我对这个解决方案不是 100% 满意,因为这些 ID 创建了“隐藏的”功能间依赖关系,但除此之外它工作正常。

以下是此设置的关键部分:

:应用程序

build.gradle

dependencies {
    implementation project(':features:feature-base')
    implementation project(':features:feature-one')
    implementation project(':features:feature-two')
}

:features:feature-base

build.gradle

dependencies {
    application project(':app')
    feature project(':features:feature-one')
    feature project(':features:feature-two')
}

导航/feature_base_nav_graph.xml

<navigation ...>
    <include app:graph="@navigation/feature_one_nav_graph" />
    <include app:graph="@navigation/feature_two_nav_graph" />
</navigation>

值/feature_base_ids.xml

<resources>
    <item name="feature_one_nav_graph" type="id" />
    <item name="feature_two_nav_graph" type="id" />
</resources>

:features:feature-one

build.gradle

dependencies {
    implementation project(':features:feature-base')
}

导航/feature_one_nav_graph.xml

<navigation
    android:id="@id/feature_one_nav_graph"
    ...>

    <fragment
        android:id="@+id/oneFragment"
        ...>
        <action
            android:id="@+id/navigateToFeatureTwo"
            app:destination="@id/feature_two_nav_graph"
            ... />
    </fragment>

</navigation>

导航

findNavController().navigate(R.id.navigateToFeatureTwo)

:features:feature-二

build.gradle

dependencies {
    implementation project(':features:feature-base')
}

导航/feature_two_nav_graph.xml

<navigation
    android:id="@id/feature_two_nav_graph"
    ...>

    <fragment
        android:id="@+id/twoFragment"
        ...>
        <action
            android:id="@+id/navigateToFeatureOne"
            app:destination="@id/feature_one_nav_graph"
            ... />
    </fragment>

</navigation>

导航

findNavController().navigate(R.id.navigateToFeatureOne)

【讨论】:

  • 虽然这个解决方案可以工作,但我相信应用程序或即时应用程序模块将是功能合并在一起的地方。基本模块只是为功能模块提供公共代码。通过活动,我可以通过深层/应用链接轻松地从一个模块导航到另一个模块,但是对于片段,我似乎找不到解决方法。
  • 对于在其他独立功能的片段之间导航,上述设置是我当前的解决方法。确实,当尝试从 feature-one 导航到 feature-two 时,在 app 模块中绑定 feature-one 而不是 feature-two 会导致运行时崩溃。如果这是意料之中的,则必须在代码中进行处理。我也会对替代方法非常感兴趣。
  • 我刚刚将 app/build.gradle 添加到答案中,因为它之前缺少。
  • 如果是这种情况,那么我猜app/build.gradle 只需要获取基本模块,因为基本模块已经拥有一切。这会起作用,但就像你说的,这不是一个 100% 令人满意的解决方案。无论如何,我会先使用这个解决方案!感谢您的建议!
  • 请注意你描述的依赖是feature project(...)而不是implementation project(...)。仅在 app/build.gradle 中包含 feature-base 不会自动包含其他功能(顺便说一句,从基础到功能添加实现依赖会创建循环依赖)。另见herehere
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-10-24
  • 1970-01-01
  • 2019-04-27
  • 2018-10-23
  • 1970-01-01
  • 2018-12-22
  • 2023-03-29
相关资源
最近更新 更多