Understanding Android Jetpack Navigation: Setup, Usage, Data Passing, Deep Links, and Source Code Analysis
This article provides a comprehensive guide to Android Jetpack's Navigation component, covering its purpose, setup with Gradle, XML graph configuration, fragment navigation code, data passing methods, deep‑link handling, and an analysis of its underlying source classes such as NavHostFragment and NavController.
Author : Qiu Junjie – Senior Android Engineer with over 5 years of development experience, responsible for JD.com product detail development.
Introduction : At Google I/O 2018, Android Jetpack was announced, consolidating components like Lifecycle, LiveData, Room, and ViewModel into a unified architecture. Among the new components, Navigation provides a visual, type‑safe way to manage fragment stacks, deep links, and argument passing.
Why use Navigation?
Simplifies fragment stack management and eliminates IllegalStateException.
Supports deep links that can jump directly to a fragment, bypassing activities.
Provides safer parameter transmission.
Setup (add to build.gradle ):
dependencies {
nav_version = '1.0.0-alpha01'
implementation "android.arch.navigation:navigation-fragment:$nav_version"
implementation "android.arch.navigation:navigation-ui:$nav_version"
}Navigation Graph (nav_graph.xml) :
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph"
app:startDestination="@id/mainFragment">
<fragment
android:id="@+id/mainFragment"
android:name="com.jd.demo.ui.main.MainFragment"
android:label="main_fragment"
tools:layout="@layout/main_fragment_2">
<action
android:id="@+id/action_to_mainFragment2"
app:destination="@+id/mainFragment2"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
</fragment>
<fragment
android:id="@+id/mainFragment2"
android:name="com.jd.demo.ui.main.MainFragment2"
android:label="main_fragment_2"
tools:layout="@layout/main_fragment_2" />
</navigation>Activity Layout (activity_main.xml) :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<fragment
android:id="@+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />
</LinearLayout>Fragment Navigation Code (MainFragment.kt) :
val view = inflater.inflate(R.layout.main_fragment_3, container, false)
val button1 = view.findViewById<Button>(R.id.button3)
button1.setOnClickListener { v ->
Navigation.findNavController(v).navigate(R.id.action_mainFragment2_to_mainFragment1)
}
return viewNavigating to a Destination – four possible calls:
Navigation.findNavController(Activity, @IdRes Int viewId)
Navigation.findNavController(View)
NavHostFragment.findNavController(Fragment)
View.findNavController()
Example: Navigation.findNavController(view).navigate(R.id.action_mainFragment2_to_mainFragment1)
Passing Data
1) Using a Bundle :
Bundle bundle = new Bundle();
bundle.putString("amount", amount);
Navigation.findNavController(view).navigate(R.id.confirmationAction, bundle);Retrieve in the target fragment with getArguments().getString("amount") .
2) Using Safe Args (type‑safe): Add the plugin to build.gradle :
apply plugin: 'com.android.application'
apply plugin: 'androidx.navigation.safeargs'Define argument in the fragment XML:
<fragment
android:id="@+id/confirmationFragment"
android:name="com.example.buybuddy.ConfirmationFragment"
android:label="fragment_confirmation"
tools:layout="@layout/fragment_confirmation">
<argument android:name="amount" android:defaultValue="1" app:type="integer" />
</fragment>Pass data:
EditText amountTv = getView().findViewById(R.id.editTextAmount);
int amount = Integer.parseInt(amountTv.getText().toString());
ConfirmationAction action = ConfirmationFragmentDirections.confirmationAction();
action.setAmount(amount);
Navigation.findNavController(view).navigate(action);Receive data:
int amount = ConfirmationFragmentArgs.fromBundle(getArguments()).getAmount();
textView.setText(String.valueOf(amount));Deep Link Support
Define deep links inside a fragment node:
<deepLink app:uri="www.jd.com" />
<deepLink app:uri="www.jd.com.*" />
<deepLink app:uri="www.jd.com/{myarg}" android:autoVerify="true" />Configure the host activity:
<activity android:name=".MainActivity">
<nav-graph android:value="@navigation/xxxxx" />
</activity>Retrieve the argument in the fragment:
String myArg = getArguments().getString("myarg");Source Code Analysis
NavHostFragment provides a container for navigation and implements NavHost . In onCreate it creates a NavController and registers a FragmentNavigator :
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Context context = getContext();
mNavController = new NavController(context);
mNavController.getNavigatorProvider().addNavigator(createFragmentNavigator());
}onCreateView returns a FrameLayout that serves as the navigation host:
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
FrameLayout frameLayout = new FrameLayout(inflater.getContext());
frameLayout.setId(getId());
return frameLayout;
}onViewCreated attaches the NavController to the root view via a tag, enabling Navigation.findNavController(view) to locate the controller:
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (!(view instanceof ViewGroup)) {
throw new IllegalStateException("created host view " + view + " is not a ViewGroup");
}
View rootView = view.getParent() != null ? (View) view.getParent() : view;
Navigation.setViewNavController(rootView, mNavController);
}
public static void setViewNavController(@NonNull View view, @Nullable NavController controller) {
view.setTag(R.id.nav_controller_view_tag, controller);
}NavController parses the nav_graph.xml via NavInflater , builds a graph of NavDestination objects, and manages the back stack. Navigator is an abstract class; concrete implementations such as FragmentNavigator and ActivityNavigator perform the actual navigation actions.
Conclusion
The Navigation component offers a powerful, safe, and visual way to handle fragment navigation, deep links, and data passing, integrating seamlessly with other Jetpack libraries like Lifecycle and ViewModel. Kotlin extensions (KTX) further simplify usage, making it essential for modern Android development.
JD Tech
Official JD technology sharing platform. All the cutting‑edge JD tech, innovative insights, and open‑source solutions you’re looking for, all in one place.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.