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

 

AndroidX Navigation: Building on the Wrong Abstraction

 

 
Isaac Udy
Android Engineer @ Xero, builds and maintains Enro, likes coffee and large Android projects.
Published: Otober 19, 2020
Tweet
Share
 

 

I recently read an article by Jesse Wilson titled “Building on the Wrong Abstraction”. If you haven’t read the article already, I would highly recommend it.

Reading Jesse’s article started me thinking about one of my favourite problems in the Android world: Navigation. Navigation is a key part of any application that has more than two screens. Which is probably most of them. But navigation on Android sucks.

To navigate from Screen A to Screen B, Screen A needs to know Screen B’s arguments, the exact names of these arguments, and whether Screen B is an Activity or a Fragment. This results in a situation where Screen A needs to know a lot about the implementation details of Screen B.

Luckily, the great people working on the AndroidX libraries also recognised this was a problem. Unfortunately, they built their solution on the wrong abstraction.

 

Understanding AndroidX Navigation

In its own words, AndroidX Navigation is “a framework for navigating between ‘destinations’ within an Android application that provides a consistent API whether destinations are implemented as Fragments, Activities, or other components.”

Alongside AndroidX Navigation, there’s a Gradle plugin called Safe Args, which describes itself as “a Gradle plugin that provides type safety when navigating and passing data between destinations.”

Putting these two together, we can avoid many of the problems present in standard Android Fragment/Activity navigation. Assuming you already have AndroidX Navigation set up in your project, here’s what the workflow looks like:

Define a Destination in the Navigation Graph:

 

 

Define an Action for that Destination:

 

 

Use the generated Directions object to perform Navigation:

 

 

This is much better than regular Activity/Fragment navigation! Directions are type-safe, and the screen performing navigation doesn’t care about the implementation of the other screen; a Fragment Destination could be swapped for an Activity Destination and the screen referencing the Directions for the Destination wouldn’t care.

 

What’s wrong with AndroidX Navigation?

AndroidX Navigation is better than regular Activity/Fragment navigation, so what’s the problem? There are a few problems that I have with AndroidX Navigation. It requires that you define arguments in XML to generate code that contains the same information you’ve already defined in XML. It uses Navigation Graphs as a centralised place for definitions and ties the Navigation Graphs to Activities rather than Applications. You can define arguments on both Actions and Destinations, which is confusing. These aren’t problems with abstraction though, they’re just design decisions I disagree with. The key abstraction problem is the relationship between Destinations and Actions.

AndroidX Navigation has the following relationships between components:

  1. Navigation Graph
    A container for defining Destinations and Actions
  2. Destination 
    Represents a screen that can be navigated to, and the arguments for that screen
  3. Action/Direction 
    Represents the intent to open a Destination (and sometimes the arguments for that Destination)

When thinking in terms of these relationships, AndroidX Navigation wants you to start with a Destination (Activity/Fragment/Other) and then create an Action that opens the Destination. This means that a screen wanting to navigate using AndroidX Navigation must have a mutual reference with the screen that it is navigating to. For Screen A to navigate to Screen B, Screen A must know about an Action that points towards a Destination for Screen B. This reference is hidden in the XML, but there’s a direct dependency between Screen A and Screen B.

The dependency flows like this:
Screen A → Action B → Destination B → Screen B

This chain of dependency is the key problem with AndroidX Navigation because it means that navigation between screens in a multi-module project becomes difficult. Screen A and Screen B can’t be in independent modules, because they need a reference to each other through the Navigation Graph.

There are ways to make AndroidX Navigation work in a multi-module project, but none of these solutions is ideal. They all introduce problems and restrictions of their own.

Here are a few approaches for using AndroidX Navigation in a multi-module project:

  1. Define the Navigation Graph in a common module
    It is possible to define a Navigation Graph in a common module and use fully qualified class names for Fragments/Activities in other modules. This will generate the Safe Args Directions that can be used for navigation. Unfortunately, this also means your Navigation Graph will be filled with unresolved class references. These unresolved class references may be missed by refactoring operations, and are hard to maintain. This might work, but the IDE shows these as errors!
  2. Define Destination IDs in a common module
    If you define all resource IDs that will be used as Destination IDs in a common module, and the Destinations in feature modules use these common resource IDs, other feature modules will be able to create Actions that reference these Destinations via the shared resource IDs. However, if you do this, you’ll have to re-define a Destination’s arguments for each Action, creating a huge amount of duplication and a fragile system where changes to the arguments for a Destination will need to be manually propagated throughout the codebase.
  3. Use deep links
    One of the best pieces of AndroidX Navigation is its support for deep links. Instead of trying to use Safe Args, you could just use deep link URIs to navigate around your Application. If you do this, however, you lose type safety, and again put yourself in a situation where it’s hard to make changes to the arguments for a particular Destination should you need to.

 

What’s the alternative?

Instead of having Actions depend on Destinations, we can flip this relationship around, here’s how that could look using the AndroidX Navigation components:

  1. Action/Direction
    Represents the intent to open a screen, and the arguments for the screen
  2. Destination 
    Represents the binding between a specific screen and an Action/Direction
  3. Navigation Graph 
    A container to hold the bindings between Destinations and Actions/Directions

When thinking in terms of these relationships, we start with an Action and then create a Destination that fulfils the Action. In the case of Screen A wanting to open Screen B, Screen A only knows about the Action for Screen B, but the Action doesn’t have a reference to the Destination, breaking the chain of dependency. This means that the Actions for a project can be defined in a common or core module, which can be used by independent modules that define Destinations and are tied together by an Application module.

The dependency flows like this:
Screen A → Action B ← Destination B → Screen B

This is the thinking that I’ve used when developing Enro, an alternative to AndroidX Navigation.

How does this work in Enro? Instead of starting with a Destination, we start by writing the equivalent of an Action or Direction, which Enro calls a NavigationKey. Assuming you’ve got Enro set up in your project, here’s what the workflow looks like:

Define a NavigationKey:

 

 

Define a NavigationDestination for that NavigationKey:

 

 

Use the NavigationKey to perform navigation:

 

 

Using a different abstraction has allowed Enro to see some big advantages over using AndroidX Navigation. Here’s a few of them:

  1. There’s less code to write
    Binding MyFragment as a Destination takes one line of code (the annotation), as compared to 8 lines of XML in the AndroidX example.
  2. Relevant code stays nearby
    The arguments for MyFragment (represented by MyDestinationKey) are right there above MyFragment’s definition in the annotation, not hidden away in XML.
  3. IDE tools work out of the box
    For example, if you wanted to find out where MyFragment is opened from, you can right-click on the reference to MyDestinationKey, which is right there in the annotation at the top of MyFragment’s definition, and use the IDE’s “Find usages” tool.
  4. Multi-module is a breeze
    In a single-module project, you might be defining NavigationKeys right next to the Fragments or Activities that use them, but in a multi-module project you can simply move these into a common module and everything continues to work as expected.

 

Conclusion

AndroidX Navigation is built on the wrong abstraction. AndroidX Navigation creates Destinations first and then builds Actions that target specific Destinations. Because of this, you are unable to cleanly support multi-module Applications using AndroidX Navigation, along with a whole host of other minor usability issues.

Enro takes a different approach. Enro creates Actions first and then builds Destinations that fulfil specific Actions. This allows Enro to be easier to use, less verbose, and integrate much more easily with multi-module projects.

Please check out Enro here, I’d love to hear your feedback and feature requests.

 

 

 

Tags: Kotlin, Android, Android App Development, Jetpack Navigation, Android Navigation

 

View original article at: 


 

Originally published: October 14, 2020

Android News
Our Engineering Roadmap
Our Engineering Roadmap

By Mark Ng

We just completed our engineering road map for our Android apps at Australia Post. Each year we get together and try to decide on what we are going to do from an engineering perspective for the next 12 months. Each team gets to decide on what should be done now, what they want to complete by the end of the year and whats on the horizon for next year.

By ProAndroidDev -
Android News
Android Activity Lifecycle considered harmful
Android Activity Lifecycle considered harmful

By Eric Silverberg

The Android activity lifecycle is one of the first architectural concepts learned by a new Android developer. Ask any Android dev and they’ll likely all have seen this handy diagram from Google: 

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

By Alejandro Weichandt

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.

By ProAndroidDev -
Android News
Custom KotlinX Serializers
Custom KotlinX Serializers

By Jobin Lawrance

Let’s say we have a third-party class that we are using as a type in one of our data class that we want to be serialized, then we have to write a custom serializable for @Serializable to work.

 

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