Navigation map interaction best practices

  • Prioritize using SupportNavigationFragment over NavigationView for simplified lifecycle management and error reduction.

  • When using NavigationView directly, ensure strict adherence to the Android lifecycle order when invoking its lifecycle methods to prevent issues like memory leaks and UI errors.

  • Invoke NavigationView lifecycle events exclusively from either the activity or the fragment to maintain order and avoid duplicate calls.

This page explains best practices for interacting with the Navigation map in your app.

Use NavigationFragment instead of NavigationView, whenever possible

NavigationFragment wraps NavigationView and automatically handles its lifecycle callbacks, so you don't need to manage them yourself. This approach is less error-prone and is the recommended way to use navigation in your app. When using NavigationFragment, don't invoke NavigationView lifecycle events directly.

If using NavigationView, use strict ordering when invoking lifecycle methods

NavigationView hosts the Navigation map and closely follows the lifecycle events as Android activities and fragments, taking specific actions when these lifecycle events are invoked. NavigationView executes multiple initializations on NavigationView#onCreate and NavigationView#onStart, and cleanups on NavigationView#onStop and NavigationView#onDestroy, as well as when other lifecycle events are processed.

NavigationView lifecycle methods have the same meaning as they do for Android activities or fragments. For example, onCreate of NavigationView roughly translates to and should be invoked by lifecycle callbacks from the Android activity or fragment. Because the NavigationView lifecycle callbacks are based on and invoked in the same order as the Android lifecycle callbacks, strict ordering of these NavigationView methods is required. Otherwise, you might experience memory leaks, UI errors, location not being updated, and other issues.

For more information about the Android activity lifecycle, see the Activity-lifecycle concepts section in the Android developer documentation.

The following table shows when other lifecycle methods should be invoked, after specified lifecycle methods:

Lifecycle method Invoked where in the activity lifecycle Invoked after which lifecycle method
onConfigurationChanged() Invoked when the UI is in the foreground and the configuration changes. Always after onStart()
onTrimMemory() Invoked when an activity is in the background. Always after onPause()
onSaveInstance() Invoked before an activity is destroyed. Always after onStop()

Don't call these lifecycle methods multiple times without calling the corresponding closing method first. In addition, keep in mind that if some of these Android lifecycle callbacks are managed by the app itself, and the NavigationView is added to the fragment after creation or start, the app should call the specific methods in proper order to correctly initialize the Navigation SDK.

For additional guidance on using these methods, see the Navigation SDK demo app.

If using NavigationView, invoke lifecycle events from the activity or fragment, not both

To keep the strict ordering of the lifecycle methods, invoke these events from either the activity or fragment lifecycle callbacks, which receive these events in order. This approach ensures that apps don't need to coordinate between fragments and activities and cause duplicate calls.

Update from SupportNavigationFragment to NavigationFragment

Starting in Navigation SDK v8.0.0, NavigationFragment replaces the deprecated SupportNavigationFragment as the standard fragment container for turn-by-turn navigation and map display.

NavigationFragment maintains full API parity with SupportNavigationFragment. To update your app, replace SupportNavigationFragment with NavigationFragment in your XML layout files and source code imports. All method signatures and getSupportFragmentManager() calls remain identical.

Update the layout XML

Replace SupportNavigationFragment with NavigationFragment in your layout XML files:

Before (v7.x and earlier):

<fragment
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/navigation_fragment"
    android:name="com.google.android.libraries.navigation.SupportNavigationFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

After (v8.0.0+):

<fragment
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/navigation_fragment"
    android:name="com.google.android.libraries.navigation.NavigationFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

If your app uses FragmentContainerView (recommended for hosting fragments), update the android:name attribute:

<androidx.fragment.app.FragmentContainerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/navigation_fragment"
    android:name="com.google.android.libraries.navigation.NavigationFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Update the application code

Replace imports and class casts in your Java or Kotlin code. Because both classes extend androidx.fragment.app.Fragment, continue using getSupportFragmentManager() to look up the fragment:

Before (v7.x and earlier):

Java

import com.google.android.libraries.navigation.SupportNavigationFragment;

public class MainActivity extends AppCompatActivity {
  private SupportNavigationFragment mNavFragment;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mNavFragment = (SupportNavigationFragment) getSupportFragmentManager()
        .findFragmentById(R.id.navigation_fragment);
  }
}
    

Kotlin

import com.google.android.libraries.navigation.SupportNavigationFragment

class MainActivity : AppCompatActivity() {
  private lateinit var navFragment: SupportNavigationFragment

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    navFragment = supportFragmentManager
        .findFragmentById(R.id.navigation_fragment) as SupportNavigationFragment
  }
}
    

After (v8.0.0+):

Java

import com.google.android.libraries.navigation.NavigationFragment;

public class MainActivity extends AppCompatActivity {
  private NavigationFragment mNavFragment;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mNavFragment = (NavigationFragment) getSupportFragmentManager()
        .findFragmentById(R.id.navigation_fragment);
  }
}
    

Kotlin

import com.google.android.libraries.navigation.NavigationFragment

class MainActivity : AppCompatActivity() {
  private lateinit var navFragment: NavigationFragment

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    navFragment = supportFragmentManager
        .findFragmentById(R.id.navigation_fragment) as NavigationFragment
  }
}
    

API parity

You don't need to make any other behavior changes or restructure your code. NavigationFragment supports all public methods, listener interfaces, and custom UI controls from SupportNavigationFragment with identical method signatures (including getMapAsync(), getNavigator(), setEtaCardEnabled(), and setStylingOptions()).