Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multi-module Navigation with Architecture Components

So I have this structure for my modules in my current app.

App Module Structure

I haven't found any official documentation on multi-module navigation yet but I found this article regarding this so here's how my gradle files are:

Feature 1 - Detail

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

Feature 2 - Detail

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

Feature 3 - Detail

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

And here are my navigation graphs:

Feature 1 - Detail

<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> 

Feature 2 - Detail

<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> 

Feature 3 - Detail

<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> 

So everything works with this kind of setup but the problem here is that to connect the module to another module, we have to add the other feature as a dependency to the current feature. Like in my case, Feature 1 - Detail can go to Feature 2 - Detail and vice versa and doing this gives me a circular dependency in gradle.

Is there another way to do multi-module navigation? I've tried using deep links but to no avail.

Any help would be appreciated! Thanks!

like image 458
Kurt Acosta Avatar asked Jul 10 '18 10:07

Kurt Acosta


People also ask

What is navigation architecture component?

The Navigation Component is made up of three major parts:It shows various destinations from your Navigation Graph. NavController (Kotlin/Java object) — This is an object that keeps track of where you are in the navigation graph.

What is multi-module architecture?

Multi-module architecture is a way of arranging code and packages in your project but on a broader view. Usually, when you create a project, you would work in a single module, but also we can create more modules within the same app.

What is multi-module Android app?

A project with multiple Gradle modules is known as a multi-module project. In a multi-module project that ships as a single APK with no feature modules, it's common to have an app module that can depend on most modules of your project and a base or core module that the rest of the modules usually depend on.


2 Answers

This is already a year-long but the library now can support this exact use-case! As of 2.1.0-alpha03, we can navigation through deep link URIs.

Instead of adding the features as implementation details to each other, we can leave them unaware between themselves and use deep link navigation.

Feature 1 - Detail - build.gradle

dependencies {     implementation project(':base') } 

Same with Feature 2 - Detail. No need for it to know the other modules.

To have inter-module navigation, we have to first define the deep link for navigating through that destination via a deepLink tag.

Feature 1 - Detail - Navigation Graph

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

Feature 2 - Detail - Navigation Graph

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

Now that we have deep links with URIs set, we can directly use this in a NavController

So in the fragment in Feature 1 - Detail, maybe on a button click? Anywhere where you have to perform navigation

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

And in Feature 2 - Detail,

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

And voila! Inter-module navigation.

At the time of writing, the latest stable release is 2.1.0-rc01.

Although I haven't tried this out on more complex projects, I love this library and I'm hoping to see this library mature more!

I created a Medium article about this. You can take a look at it. Cheers!

like image 199
Kurt Acosta Avatar answered Nov 02 '22 07:11

Kurt Acosta


One of the approaches that might be useful is to create a completely new independent module (e.g ":navigation" module) and move all navigation.xml files from all other modules to it. Then we depend on that new (":navigation") module in all other modules where navigation related stuff is needed, and we will be able to access its R.navigation or generated argument classes, etc.

Since the new (":navigation") module doesn't know about anything else in the project IDE will mark red any fragment, activity and other classes we use in navigation.xml files, that are defined outside in other modules but as long as we use full class names (com.exampel.MyFragment) it will compile and work.

<?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 

This creates "hidden" dependency to all classes we want to navigate to in a way that we need to know class names and potentially arguments, and we have to maintain it manually but it allow us to easily separate navigation in independent module.

like image 28
doolle89 Avatar answered Nov 02 '22 09:11

doolle89