FragmentManager
通过mBackStack
管理Fragment的回退栈,当对Fragment进行add/replace
时,通过FragmentTransaction#addToBackStack
可以将Fragment添加到mBackStack。
Navigation的NavController
也持有自己的mBackStack,与FragmentManager不同的是,除了Framgent以外,NavGraph
实例也会被添加入栈。
初始状态
<?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"
android:id="@+id/main_navigation"
app:startDestination="@id/home_fragment">
<fragment
android:id="@+id/home_fragment"
android:name="com.example.HomeFragment" />
</navigation>
如上定义的Navigation,其初始堆栈如下
home_fragment
虽然作为初始fragment加入back stack,但栈的底部不是它而是NavGraph
。
Fragment跳转
1. 跳转同级Fragment
<navigation>
下同级fragment之间跳转。的home_fragment跳转到detail_fragment
<?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"
android:id="@+id/main_navigation"
app:startDestination="@id/home_fragment">
<fragment
android:id="@+id/home_fragment"
android:name="com.example.HomeFragment" />
<fragment
android:id="@+id/detail_fragment"
android:name="com.example.DetailFragment" />
</navigation>
2. 跳转子NavGraph
Navigation允许NavGraph嵌套子NavGraph。子NavGraph与子Fragment是同一级,通过NavController#navigate
可以跳转到子Fragment以及子NavGraph,跳转子NavGraph相当于跳转到其默认的startDestination
。
<?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"
android:id="@+id/main_navigation"
app:startDestination="@id/home_fragment">
<fragment
android:id="@+id/home_fragment"
android:name="com.example.HomeFragment" />
<navigation
android:id="@+id/login_navigation"
app:startDestination="@id/login_fragment">
<fragment
android:id="@+id/login_fragment"
android:name="com.example.LoginFragment" />
</navigation>
</navigation>
如上,指定desId
为login_navigation
,可以从home_fragment
跳转到子navigation的login-fragment
。
上图中可以看到,子NavGraph也一并入了栈。
3. 跳转父NavGraph
父Fragment通过destId
往子级跳转,只能跳转NavGraph而不能跳转具体子Fragment。但是反之,子Framgent可以指定destId
跳转到具体父Fragment。
如下,从login_fragment
跳转到home_fragment
,此时没有压栈父NavGraph。
Deep Link
Navigation的deep link机制,允许我们通过PendingIntent
或者URL
,直接跳转到指定destination(Fragment/Activity),deep link根据跳转的启动条件分为三类
1. Explicit deep link
https://developer.android.com/guide/navigation/navigation-deep-link#explicit
来自外部的startActivity请求经常携带FLAG_ACTIVITY_NEW_TASK
的flag。如下,通过FLAG_ACTIVITY_NEW_TASK
的方式跳转example://nav_sample/
:
<?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"
android:id="@+id/a_navigation"
app:startDestination="@id/a_fragment">
<fragment
android:id="@+id/a_fragment"
android:name="com.example.AFragment" />
<navigation
android:id="@+id/b_navigation"
app:startDestination="@id/b_fragment">
<fragment
android:id="@+id/b_fragment"
android:name="com.example.BFragment" />
<navigation
android:id="@+id/c_navigation"
app:startDestination="@id/c_fragment">
<fragment
android:id="@+id/c_fragment"
android:name="com.example.CFragment">
<deepLink app:uri="example://nav_sample/" />
</fragment>
</navigation>
</navigation>
</navigation>
此时NavController的back stack如下,可以看到各级的start destination
都进入栈中。
2. Implicit deep link
https://developer.android.com/guide/navigation/navigation-deep-link#implicit
对于不带有FLAG_ACTIVITY_NEW_TASK
的startActivity,back stack比较简单,start destination没有入栈。
3. Deep link within Activity
Navigation 2.1.0之后,同一个Activity内部的Fragment也可以进行deep link跳转。
相对于destId的方式,使用deep link可以跳转到任意层级的具体的fragment,此时的back stack与不带有FLAG_ACTIVITY_NEW_TASK
的跨Activity启动类似。
前一个例子中,从a_fragment
跳转到c_fragment
时:
Popup、popback
点击系统back键
和点击Toolbar左上的返回键
都可以回退,但是行为有不同
1. back键
按系统back键返回时,会跳过NavGraph返回到最进的fragment
当栈的下方没有其他fragment时,则退出Activity,如下

2. Toolbar返回键
Toolbar返回时,不会跳过NavGraph,而且回退过程有可能创建start destination。
例如从c_fragment
返回时,b_navigation
的start destination在back stack中缺失,所以会创建b_navigation。
PopUpTo
跳转到指定画面时,可以通过PopUpTo
让其先返回到某个destId
后再跳转。popUpTo在某些业务场景中非常实用:
1. 清空back stack后跳转
例如当我们跳转到需要鉴权的画面时,如果此时未登录可能先跳到login画面,如果用户不想登录,点击back键可以直接退出Activity而不是返回前一个画面
<?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"
android:id="@+id/main_navigation"
app:startDestination="@id/home_fragment">
<action
android:id="@+id/action_login"
app:destination="@+id/login_navigation"
app:popUpTo="@id/main_navigation"
app:popUpToInclusive="true"
app:launchSingleTop="true"/>
<fragment
android:id="@+id/home_fragment"
android:name="com.example.HomeFragment" />
<navigation
android:id="@+id/login_navigation"
app:startDestination="@id/login_fragment">
<fragment
android:id="@+id/login_fragment"
android:name="com.example.LoginFragment" />
</navigation>
</navigation>
如上,action
中的popUpTo指向NavGraph,destination指向login_fragment
。在home_fragment
中使用action_login
跳转时
其中,popUpToInclusive
指定true时,中间的back stack全部清除,如果指定false,则会留下main_navigation
2. 跳转到指定页面后无法按原路返回
例如一些表单填写场景,但完成表单并提交后,就只能返回首页,而不能再按原路返回上一页。
如下,从form2_fragment
跳转到complete_fragment
时,使用popUpTo
清空表单fragment防止原路返回。
<?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"
android:id="@+id/main_navigation"
app:startDestination="@id/main_fragment">
<fragment
android:id="@+id/main_fragment"
android:name="com.example.MainFragment" />
<navigation
android:id="@+id/form_navigation"
app:startDestination="@id/form1_fragment">
<fragment
android:id="@+id/form1_fragment"
android:name="com.example.form.Form1Fragment">
<action
android:id="@+id/action_form1_to_form2"
app:destination="@+id/form2_fragment"
app:launchSingleTop="true"/>
</fragment>
<fragment
android:id="@+id/form2_fragment"
android:name="com.example.form.Form2Fragment">
<action
android:id="@+id/action_form2_to_complete"
app:destination="@+id/complete_fragment"
app:popUpTo="@id/form_navigation"
app:popUpToInclusive="false"
app:launchSingleTop="true"/>
</fragment>
<fragment
android:id="@+id/complete_fragment"
android:name="com.example.form.CompleteFragment" />
</navigation>
</navigation>