preface

Androidx Navigation is the official library for in-app Navigation in Android. The latest version is 2.3.0-Beta01 (2020.05.20).

A lot of people didn’t like Navigation because the design didn’t match the developer’s expectations, and it caused a level fragment rebuild when toggle back and forth between managing the “level interface”. There is a plan on the web to rewrite Navigator for this problem, and most people will simply assume that Navigation cannot save the fragment state because of the use of replace (which I used to think).

This article covers the functional boundaries of Navigation, simple use, advanced use techniques (such as sharing viewModels with fragments within an activity section, modularization), and a discussion of so-called “design issues” with Navigation

Those who already know the usage and functional boundaries of Navigation can skip the first two parts

Due to the longer content of the article, the modular part has been written separately, and the address is here

A world without Navigation

In Android, activities and fragments are the primary view controllers, so interinterface transitions revolve around the activity/fragment

/ / jump activity
val intent = Intent(this, SecondActivity::class.java)
intent.putExtra("key"."value")
startActivity(intent)

 / / jump fragments supportFragmentManager.commit {  addToBackStack(null)  val args = Bundle().apply { putString("key"."value")} replace<HomeFragment>(R.id.container, args = args) } Copy the code

If the project is large, we might extract the KEY as a constant and fill in static methods in the activity and fragment to tell the caller what parameters are required for the interface

// SecondActivity
companion object {
    @JvmStatic
    fun startSecondActivity(context: Context, param: String) {
        val intent = Intent(context, SecondActivity::class.java)
 intent.putExtra(Constant.KEY, param)  context.startActivity(intent)  } }  // HomeFragment companion object {  fun newInstance(param: String) = HomeFragment().apply {  arguments = Bundle().also {  it.putString(Constant.KEY, param)  }  } } Copy the code

As you can see, thanks to Kotlin’s extension functions, the code for jumping between interfaces is simple enough

but

  • What about jumping animations on every screen?
  • When you’re taking on a larger project, how can you quickly figure out how to jump between interfaces?
  • In a single-activity project, how do you control that several related fragments share the same ViewModel scope, rather than the entire activity?
  • Does the interface between components change?

Introduction of Navigation

From the 19I/0 Conference

The Jetpack navigation component is a set of libraries, tools, and guides that provide a powerful navigation framework for in-app navigation

It is a set of libraries that encapsulate an API for in-app navigation

Introduce dependencies as follows

dependencies {
  def nav_version = "2.3.0 - beta01"

  // Java language implementation
  implementation "androidx.navigation:navigation-fragment:$nav_version"
 implementation "androidx.navigation:navigation-ui:$nav_version"   // Kotlin  implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"  implementation "androidx.navigation:navigation-ui-ktx:$nav_version"   // Dynamic Feature Module Support  implementation "androidx.navigation:navigation-dynamic-features-fragment:$nav_version"   // Testing Navigation  androidTestImplementation "androidx.navigation:navigation-testing:$nav_version" }  Copy the code
destination

It supports jumping between fragments, activities, or custom destinations

Navigation UI

Navigation UI library supports Drawer, Toolbar, and other UI components

It is a set of tools to visualize the navigation logic of the admin interface in Android Studio

Android Studio provides tools for visual management

Now that we have a preliminary understanding of Navigation, let’s look at the functional boundaries of Navigation

What does Navigation do

  • Simplify the configuration of the interface jump
  • Management return stack
  • Automating Fragment Transaction
  • Type-safe passing of parameters
  • Manage transitions
  • Simplify the deep link
  • Manage navigation centrally and visually

Navigation working logic

Navigation has three main parts

  • Navigation Graph
  • NavHost
  • NavController

Navigation Graph

The Navigation Graph is a new resource type that is an XML file that centrally manages Navigation

Navigation Graph

“Each interface in the Navigation Graph is called: Destination”. It can be a fragment, an activity, or a custom Destination

Navigation manages hops between destinations

Click Destination to see the configuration of deep Link and other information on the right side of the screen

destination attitude

“The arrow line in the Navigation Graph is called: Action”, which represents the different paths between destinations

Click on the Action to see the detailed configuration of the Action on the right side of the screen, animation, jump passing parameters between Destination, Action return stack, Launch Options

action attributes

A Navigation Graph is like a Navigation Graph, and Destination and Action are like points and edges in Graph theory.

NavHost

NavHost is an empty container that displays the destination in the Navigation Graph. The navigation component provides a default NavHost implementation of NavHostFragment, which displays the Fragment Destination

NavHostFragment is the class in navigation-Fragment

NavHostFragment

It provides an area that can be navigated independently, and it looks something like this when used

NavHostFragment use

All fragment destinations are managed by NavHostFragment, which acts as a container

Each NavHostFragment has a NavController that defines the navigation in the Navigation host. It also includes the Navigation Graph and navigation states (such as current position and return stack), which are saved and recovered along with the NavHostFragment itself

NavController

The NavController helps the NavHost manage navigation. It holds both the NavGraph and the Navigator internally (indirectly through the NavigatorProvider).

NavGraph, which determines the jump logic between interfaces, is usually created under Android Resources, but also supports dynamic creation from code

The Navigator defines a mechanism for navigating within an application. Its implementation classes are ActivityNavigator, DialogFragmentNavigator, FragmentNavigator, and NavGraphNavigator. Of course, developers can also customize the Navigator. Each Navigator has its own navigation policy. For example, ActivityNavigator uses starActivity to navigate

conclusion

To help you understand the dependencies, here is an illustration from KunMinX’s column relearning Android Navigation

Learn Android again: Without Jetpack Navigation, be sure to appreciate the beauty of declarative programming!

The XML file that we create in res/navigatoin is called Navigation Graph.

Each node inside is called a Destination(similar to a graph theory point), which corresponds to an activity/fragment/dialog and represents an on-screen interface

The line connecting Destination to Destination is called an Action(similar to an edge in graph theory). It is an abstraction that hops from one interface to another. It can be configured to animate the jump, pass parameters, and return the stack

NavHost is a container that displays the Navigation Graph. The implementation class is NavHostFragment. Each NavHostFragment holds a NavController

The NavController is the master of navigation, encapsulating such methods as navigate navigateUp popBackStack

Navigator is an encapsulation of hops between destinations. Because a Destination can be an activity or fragment, there are implementation classes such as ActivityNavigator and FragmentNavigator that implement specific interface jumps

Navigation tips

Dialog Destination

Navigation 2.1.0 introduced to implement navigate to a DialogFragment

It’s easy to use, using the dialog tag, and everything else is the same as the fragment

dialog destination

Share viewModels in the same graph

We all know that fragments can share viewModels at the activity level, but for a single activity project, this means that all fragments can get the shared ViewModel. On the principle of least knowing, this is not a good design

You want to share the ViewModel in some fragments

Navigation 2.1.0, the official introduction of a shared ViewModel within the Navigation Graph, which makes the scope of the ViewModel more detailed and enables better isolation between businesses

Very simple to use

// kotlin
val viewModel: MyViewModel by navGraphViewModels(R.id.my_graph)
Copy the code
// java
NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.my_graph);
MyViewModel viewModel = new ViewModelProvider(backStackEntry).get(MyViewModel.class);
Copy the code

Safe Args

What is Safe Args

What is Safe Args?

It is a Gradle plugin that generates code from the navigation file to help developers safely pass data between destinations

So why design such a plug-in?

We know that type mismatches or other exceptions that occur when passing data using a bundle or intent are only discovered in Runtime, but Safe Args moves validation to compile time

Why Safe Args

Using Safe Args requires manually importing the plug-in

buildscript {
    repositories {
        google()
    }
    dependencies {
 def nav_version = "2.3.0 - alpha06"  classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"  } } Copy the code

We mentioned earlier that Safe Args generates code

If you want to generate Java code, add it to the build.gradle of your app or other Module

apply plugin: "androidx.navigation.safeargs"
Copy the code

If producing Kotlin code, add

apply plugin: "androidx.navigation.safeargs.kotlin"
Copy the code

The arguments are configured in the action, so we just add the argument tag

<action android:id="@+id/startMyFragment"
    app:destination="@+id/myFragment">
    <argument
        android:name="myArg"
 app:argType="integer"  android:defaultValue="1" /> </action> Copy the code

Navigation supports the following types

Parameter types supported

After Safe Args is enabled, the generated code creates the following type-safe classes and methods for each operation and each send and receive destination

  • Create a class with destination name + Directions for actions that send destination. For example we send destination for SpecifyAmountFragment, then will generate SpecifyAmountFragmentDirections class. This class creates a method for each action of the destination
  • Create an inner class for each action that passes arguments. If the action is called confirmationAction, the confirmationAction class is created. If the action parameter does not have a default value, the developer is required to use this class to set the parameter
  • To receive destination, create a class with the name destination + Args. For example, if our destination is ConfirmationFragment, then the ConfirmationFragmentArgs class will be generated. Using this classfromBundle()The destination method retrieves the parameters passed from the destination

The following code shows how to pass parameters in sending destination

override fun onClick(v: View) {
   valamountTv: EditText = view!! .findViewById(R.id.editTextAmount)   val amount = amountTv.text.toString().toInt()
   // Set the parameters in the corresponding action
   val action = SpecifyAmountFragmentDirections.confirmationAction(amount)
 v.findNavController().navigate(action) } Copy the code

Let’s show you how to retrieve the passed parameter from the destination. Can Kotlin use by navArgs() to retrieve the parameter

// kotlin
val args: ConfirmationFragmentArgs by navArgs()

override fun onViewCreated(view: View, savedInstanceState: Bundle?). {
    val tv: TextView = view.findViewById(R.id.textViewAmount)
 val amount = args.amount  tv.text = amount.toString() } Copy the code
// java
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    TextView tv = view.findViewById(R.id.textViewAmount);
    int amount = ConfirmationFragmentArgs.fromBundle(getArguments()).getAmount();
 tv.setText(amount + "") } Copy the code

Nested navigation graph

Some destinations are often used in combination and are called repeatedly in multiple places. For example, separate login processes, subsequent forgetting passwords, changing passwords, and other destinations can be used as a whole

In this case, we use a Nested navigation graph. Select a set of destinations that can be used as a whole (hold down shift to select), then right-click and select Move to NestedGraph > New Graph. This generates a nested Navigation Graph. Double-click on the nested Navigation Graph to see the internal destination

Create a nested Navigation Graph

If you want to reference the graph in another Module, you can useincludeThe label

Use the include
Use the include

Global action

You can create a common action for multiple destinations, for example you might want to navigate to the same interface in different destinations

You can use global action for this case

Select a destination, right-click, and choose Add Action > Global. An arrow will appear to the left of the destination

Global action

To navigate, simply pass in the resource ID of the global action to the Navigate method

viewTransactionButton.setOnClickListener { view ->
    view.findNavController().navigate(R.id.action_global_mainFragment)
}
Copy the code

Conditions for navigation

During development, we might come across a situation where one destination hops to a different destination based on a condition

For example, some destinations require the user to be logged in to enter, or after the game is over, victory and defeat jump to different destinations

Let’s use an example to show how Navigation handles this scenario

In this example, the user tries to jump to the resource page. If the user is not logged in, the user needs to jump to the login page

graph

We use LoginViewModel to save the login status, click the button from ProfileFragment to jump to ProfileDetailFragment, judge the login status in this interface, if it is unauthorized, jump to LoginFragment. If authorized, a welcome message is displayed

ProfileDetailFragment

Check the login status on the login page. If the authorization succeeds, return to ProfileDetailFragment. If the authorization fails, a login failure message is displayed

If you click “Back” on the login page, the ProfileFragment page is displayed

LoginFragment

Deep Links

In the development process, we may encounter such requirements. We need to let the user parachuting directly to a specific page when opening the app (for example, clicking the notification bar to jump to a specific article), or we need to jump from a destination to a destination deeper in other processes, as shown in the figure below. The FriendList interface is switched to the Chat interface

The above requirement is called deep Link, and Navigation supports both deep link hops.

Explicit deep link

In the manifest Activity -> Intent-filter TAB, add the action, category, data tags, and so on. The intent that meets the criteria can be opened

See the official document for details. We won’t go into details here. We will just focus on how to build an intent with Navigation

val pendingIntent = NavDeepLinkBuilder(context)
    .setGraph(R.navigation.nav_graph)
    .setDestination(R.id.android)
    .setArguments(args)
    .createPendingIntent()
Copy the code

If there have been a NavController, can pass NavController. CreateDeepLink () method to create deep link

Implicit deep link

Support deepLink tag in navigation Graph

For example, to jump from FriendListFragment to ChatFragment, you can add deepLink TAB under Graph ChatFragment

<fragment
    android:id="@+id/chatFragment"
    android:name="com.flywith24.bottomtest.ChatFragment
 android:label="ChatFragment">
    <argument  android:name="userId"  app:argType="string" />  <deepLink  android:id="@+id/deepLink"  app:uri="chat://convention/{userId}" /> </fragment> Copy the code

The NavController#navigate() method supports incoming uris

navigate to deep link

So it can be called directly

val userId = "1111"
findNavController().navigate("chat://convention/$userId".toUri())
Copy the code

modular

Use Navigation + Dynamic Feature Module for modularization

Discussion on Navigation Design

Do you really know Fragment Replace

Androidx frament replace behavior do you really understand?

In this section, we will analyze the SaveState ViewModel and look at the Fragment lifecycle from the source code perspective AndroidX Fragment1.2.2 source analysis has been analyzed

So let’s go straight to the conclusion

After fragment replace, “the previous fragment will perform onDestroyView but not onDestroy”. That is, the fragment itself is not destroyed, but its internal view is destroyed

FragmentManager moveToState method according to the condition will perform in front of the trigger fragments onDestroyView fragmentStateManager. SaveViewState () method to save the view Status (1.2.2, old version method name slightly different)

Since the fragment itself is not destroyed, its members are not destroyed either

The state of the view is restored, but the member state is not changed, so the fragment can be restored to its original state after replace

So what about the “design problem” with Navigation?

The fragment that was rebuilt

To create a project in Android Studio, select “Bottom Navigation Activity” for the template to get the flat TAB toggle template implemented with Navigation. We switch from HomeFragment to DashboardFragment, and then back to HomeFragment. Log as follows

Use navigation to toggle over the fragment

You can see that the HomeFragment is rebuilt, “old instance (e6c266), new instance (c3e49cc)”

The fragment is rebuilt, that’s why!

So why is this happening? Let’s look at the source code

Navigate method
Create a new fragment instance using reflection

As you can see from the source code, it creates a new fragment instance internally through reflection, which causes the state inside the fragment to be unrecoverable

However, if navigation has no horizontal relationship with all destinations, in other words, within a return stack, this design is fine

However, sometimes we want to use navigation to manage some horizontal interface, such as BottomNavigation

BottomNavigation does not comply with Material Design

Issuetracker has such an issue, notice when it was raised

issue

The main meaning is that the current bottom TAB Navigation does not meet the specification of Material Design

  • Save scroll position between labels
  • Each label should have its own return stack

There are other issues of the same type

Same type of issue

Ian Lake gives a detailed answer to this question in the issue

I’m going to give you a brief introduction to Ian Lake. I don’t know what his position is, but he’s been active as the head of Both The Fragment and Navigation, speaking at Google I/O conferences and the Android Dev Summit. Examples are fragments of past, present, and future, single activity items

The official answer

A single FragmentManager does not support multiple return stacks. Existing fragment apis do not support this. Developers have to implement multiple return stacks themselves【 Back Jetpack Fragment】 from the perspective of source code Fragment return stack with more return stack demoDemo provided in this article)

But he offered short – and medium-term options

  • Short term: Provides a public example of a multi-return stack implementation using the current API (i.e., a separate NavHostFragment and navigation graph for each bottom navigation item). The demo is here, and the core logic is in navigationExtension.kt

  • Intermediate: Build proper apis on fragments so that they can properly support multiple return stacks, including correctly saving and restoring saved and non-configured instance states for all fragments on all return stacks. This work is currently at an exploratory stage and, although I hope, I cannot provide a timetable or guarantee that it will be successful

The above response occurred in February 2019

In October 2019, Ian Lake responded to developers’ questions again

Official Supplement 1
Official Supplement 2

The multi-return stack support plan requires three steps

  • Provide an API for fragments to ensure that the state of multiple stacks of fragments can be saved
  • The NavController API provides a generic framework that allows any Navigator (including FragmentNavigator) to support multiple return stacks
  • The new API provided in NavigationUI allows you to control setupWithNavController() when using the BottomNavigationView or NavigationView, whether it is a single return stack (current mode) or multiple return stacks.

He then replied to the developers on the 70th floor and made it clear that if A single NavHostFragment/Navigation Graph/FragmentManager could support multiple return stacks, there would be no problem navigating from A or B to C

And then we move on to 2020

The latest response in 2020

On January 30, Ian Lake responded again

The Fragment 1.4.0- Alpah01 and Navigation 2.4.0- Alpha01 will provide multiple return stack support

This concludes the discussion of the so-called “design issues” with respect to Navigation

Let us know what you think in the comments section

Jetpack series

The main components of the Jetpack series have been covered here. If you haven’t seen it before, please check it out. I have sorted out the blog and posted it here

  • 【 Back Jetpack】 The dependencies and transitives of the main components of Jetpack

  • Using Activities and Fragments in AdroidX

  • Do you really know how to use fragments? Fragment FAQ and new ways to use fragments on androidx
  • AndroidX Fragment1.2.2 source code analysis
  • Jetpack OnBackPressedDispatcher Fragment return stack preparation
  • 【 Back Jetpack Fragment】 from the perspective of source code Fragment return stack with more return stack demo
  • 【 Back Jetpack】 Never lose the status of Androidx SaveState ViewModel-SaveState analysis
  • Even if you don’t use MVVM, understand the ViewModel — the functional boundaries of the ViewModel
  • Everything based on Lifecycle is obscure and useful
  • LiveData on the back of Jetpack: ViewModel’s data-driven arm really smells good
  • DataBinding on Jetpack: When will data-driven magicians turn around?
  • With The Jetpack Navigation on your back, you can go wherever you want


About me

I am a Fly_with24

  • The Denver nuggets

  • Jane’s book

  • Github