Loading...
Home
  • Tech Blogs
  • Videos
  • Conferences
    • Droidcon News
    • Upcoming Conferences
    • Become a Partner
    • Past Events
    • Keep Me Informed
    • Diversity Scholarships
  • Community
    • droidcon Team
    • How to Hold a Droidcon
  • Android Careers
Sign In

Global CSS

 

Our Safe Approach to Android Jetpack Navigation in a Multi-Modular App

 

 
Alejandro Weichandt
Mobile Apps Developer. Currently working at Sync. Money
Published: February 22, 2021
Tweet
Share
 
 

It has been a year since we started working on the Android version of the mobile app at Sync. During that year, we faced more than once that moment when we had to choose which path to follow on an Architectural decision. This story is about Navigation.

 

 

Photo by Kaleidico on Unsplash

 

At the moment the project was born, we agreed to follow whenever possible some guidelines (and I’ll save the story about those decisions for another time).

  • We must follow Clean Architecture Design
  • We agreed to go with a multi-modular project
  • Try to follow Android suggested best practices

Taking them into consideration, when the time came to choose which navigation system to go with, we opted for Android Jetpack Navigation. Here it goes more or less the list of benefits/drawbacks we considered at that time.

 

Benefits

  • Declarative navigation by XML file
  • UI design in Android Studio
  • Safe Arguments support
  • Deeplink support

 

Drawbacks

  • At that time library was in alpha stage
  • Barely documented at that time

To explain some of the architecture we ended up building, I created this ToDo demo application for you to check. It has been built using the same principles we follow at Sync. It contains:

  • Base domain and view modules to place architecture abstractions
  • Feature domain and view modules. In this example, we only have Todo
  • App module, where we join all together and build the final app.

With that in mind, let’s now dive into the most important topics we have faced during this journey.

 

Image for post

ToDo example navigation graph

 

Base navigation architecture abstraction

First step was to come with a basic navigation abstraction simple enough to navigate back and forth. Simplest model we could think of was a navigation stack, where you can push/pop fragments as you go.

Resultant interface was about having a delegate Router component who will drive fragment’s navigation to destination Routes, being able to push/pop them.

 

 

Although this abstraction was made with Jetpack navigation in mind, you can easily change the implementation to use a different one.

Sending the instance allows us to identify the source route and also access the Jetpack’s navigation controller as well

Next step is to go into each of feature module’s fragments and define the specific Router and Routes required to work. Following the demo app example we have 4 fragments to work with (and you can check the navigation reference in the above image):

  • TODOs List
  • Add new TODO Form
  • TODO details
  • Delete TODO confirmation popup

To simplify the example we are going to just focus on the list and form. Resultant routers look like the following code:

 

 

Each of the Routers with destination routes has an IRoute sealed class with all the possible destinations in it, with a push function to be used. By having this we can identify and extend every specific source router push function without collision issues.

Let’s look now at how our IRouter and IRoute abstractions are being used by the list Fragment in example:

 

 

In the Demo repository, we have some extra support classes such as StackFragment and RoutedViewModelDelegate to enable navigation from viewModels and improve code reusability at the same time.

LiveDataEvent refers to a single event to be triggered by a LiveData. This class can be found in the demo repository as well.

By having the injected router inside the fragment, we are able to navigate to available routes. Also consider that, by having routes defined in the Router definition, we are also able to let the navigations decisions up to the fragment’s viewModel.

Final step is to implement those abstractions with Jetpack and inject them. As we wanted to avoid Jetpack navigation dependencies in the feature modules or base library, we built all the implementations in the app module.

So how do we bind all these abstractions using Jetpack? let’s look at the example again:

 

 

Main idea is to create a Router implementation related to every nav_graph you have in your app and take advantage of all the support classes Android Jetpack Navigation provides for you.

This AppRouter follows the navigation graph shown in the previous image, with all 4 fragment routers required implementations in it. As you can see, the only responsibility here is to map between Routes and actual navDirections from Jetpack.

Here are the guides on how to integrate Jetpack Navigation and Safe Args, and Hilt for Dependency Injection.

After having all these steps in place we are now ready to navigate inside our app!

 

Passing arguments in a safe way

First requirement we met was to be able to pass arguments between fragments. As Jetpack provides SafeArguments support we wanted to take advantage of them. So let’s follow the steps again. First one was to provide a simple abstraction.

 

 

Each of the Routers with arguments to read extends IArgsRouterimplementation with the attached child Args class inside.

An important note is we choose Serializable as the base abstraction for arguments as it’s supported by AndroidX navigation, and it’s safe to use it in our domain classes as they are Kotlin pure modules.

To display the usage example let’s now focus at the navigation between List and Details from the Demo, as we need to send the Todo ID.

 

 

So first step is to enable arguments definition in the Routes as the list does. We use data classes for these route definitions.

On the destination side, by having the injected router inside the fragment, we are able to retrieve fragment’s arguments if any as simple as calling its args() getter, now available from the IArgsRouter .

And as I told you at the beginning, implementation has no secrets as Jetpack provides all the support classes for you to use. The only thing to do is to map between Args and SafeArgs support classes

 

 

Here are the guides on how to enable Safe Args as well

 

Splitting navigation into subgraphs and it’s hidden secrets

One thing you may notice from the Demo example is that Details fragment lives in a different navigation graph. As your app grows in screens you may want to rearrange the navigation and use more than one navigation graph to improve visibility, and also to work with the navigation scope for view model’s lifecycle. In our team, as soon as we started doing it we noticed SafeArgs were not available for actions between graphs unless you explicitly declared them in the action itself. This requirement brought us issues when updating arguments, as sometimes we forgot to update the related actions, and became a big pain. Lucky us, we found a useful trick to overcome this issue.

If you were paying attention in the previous section, to pass arguments to the details route, the AppRouter implementation used DetailsNavGraphDirections instead of TodoListFragmentDirections. Now it’s the time to explain why.

A simple rule we started to follow was to declare a global action on each navigation graph to be used by other graphs when wanted to navigate into it. Yes, it means a global action with the navigation graph itself as destination. No trick here, but in terms of visibility, when the start destination of the graph updates its arguments, is easy to notice the global action must be updated as well, as it’s written just above that fragment definition.

Here comes the trick. After having this global action in place, we can use it on a different graph by only writing its id and destination. By using the same id we are able to replace the fragment support action generated by Jetpack with the global one. As they share the same id, but only the global one contains the arguments definition, SafeArgs will become available on that support class.

An example of this rule can be found in the demo app as well, buts let’s look at the important code here:

 

 

Yet again, the guides on how to start using nested graphs and global actions

 

Back Stack comes to help

Another problem we started to face was about passing data back as a result from a Fragment to its parent.

First attempt was to use shared view models to maintain data between them, but again, started to become a pain for us as we had to create too much boilerplate code maybe just for a single boolean result. But the back stack support came into Jetpack navigation, and we decided to give it a try.

First step again was to look for a simple abstraction. Best ones we can thought were pushForResult(Fragment, Route, callback) , which will be used on the parent Fragment, and popWithResult(Fragment, result) on the other side.

Implementation details are in BaseRouter from the demo example of course. But the approach was to maintain a LiveData in the Android BackStackEntry as was available to use with the Jetpack Navigation.

You can find the full example in the navigation between the Details view and its Delete popup. But again, here comes the resume:

 

 

You can read more about the back stack in android navigation here

 

Final thoughts

A year has passed, and we are still working with Android Jetpack navigation over this Architecture that I tried to briefly explain. The real implementation has different names and some extra differences with the Demo one, like the lack of the included support classes. Of course, we do have some extra solutions to more specific problems that appeared on the way, but the goal of this post was to show the most usual ones.

At that time we really wanted to have some guidelines on how to integrate Jetpack navigation without being totally coupled to it. So hope this “guide” helps future devs looking for it.

More posts about our story at Sync. are yet to come. If you are interested at what we do, feel free to reach us here. We are always looking for new people to join in our journey.

Thanks for reading!

 

 

 

Tags: Android, Jetpack Navigation, Android Navigation, Android Development, Android Architecture

 

View original article at: 


 

Originally published: February 15, 2021

Android News
Compose CameraX on Android
Compose CameraX on Android

By Peng Jiang

Android new UI toolkit Jetpack compose is in beta now, which has all the features you need to build production-ready apps. CameraX is another Jetpack support library, which let you control the camera easier. As compose is still under development, lots of the views are still not available the compose way.

By ProAndroidDev -
Android News
Getting… your BottomSheetScaffold working on Jetpack Compose Beta 03
Getting… your BottomSheetScaffold working on Jetpack Compose Beta 03

By Carlos Mota

It’s Monday, no releases this week, and… there’s a new version of Jetpack Compose — beta 03—available. What a perfect time to just increment 02 to 03 and see what’s new. The API is (almost) final so after updating from alpha to beta there weren’t any big changes to do. However, and remember that’s still in development, there’s always something that I need to update. 

By ProAndroidDev -
Android News
Noisy Code With Kotlin Scopes
Noisy Code With Kotlin Scopes

By Chetan Gupta

Scopes make your code more readable? think again... You are going to encounter these scope functions namely let, run, apply, also, within every Kotlin codebase, along with all the mischievous ways developers exploit their usage from the way they were intended for. Let see how popular opinion on those ends up just as a code noise.

By ProAndroidDev -
Android News
Improving Android DataBinding with Bindables library
Improving Android DataBinding with Bindables library

By Jaewoong Eum

DataBinding is one of the most important factors for MVVM architecture. The basic concept of DataBinding is to link the view and view model via observer patterns, properties, event callbacks, etc. Linking and automating communication between the view via the bound properties or something in the view model has a lot of benefits in the MVVM architecture concept.

By ProAndroidDev -
droidcon News

Tech Showcases,

Developer Resources &

Partners

/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/home-details/EmployerBrandingHeader
EmployerBrandingHeader
https://jobs.droidcon.com/
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/jobs-droidcon/jobs.droidcon.com
jobs.droidcon.com

Latest Android Jobs

http://www.kotlinweekly.net/
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/kotlin-weekly/Kotlin Weekly
Kotlin Weekly

Your weekly dose of Kotlin

https://proandroiddev.com/
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/pad/ProAndroidDev
ProAndroidDev

Android Tech Blogs, Case Studies and Step-by-Step Coding

/detail?content-id=/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Zalando/Zalando
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Zalando/Zalando
Zalando

Meet one of Berlin's top employers

/detail?content-id=/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Academy for App Success/Academy for App Success
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Academy for App Success/Academy for App Success
Academy for App Success

Google Play resources tailored for the global droidcon community

Follow us

Team droidcon

Get in touch with us

Write us an Email

 

 

Quicklinks

> Code of Conduct

> Terms and Conditions

> How to hold a conference

> FAQs

> Imprint

Droidcon is a registered trademark of Mobile Seasons GmbH Copyright © 2020. All rights reserved.

powered by Breakpoint One