【Android Jetpack】彻底弄清Navigation的BackStack如何变化

在这里插入图片描述
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>

如上,指定desIdlogin_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>

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/vitaviva/article/details/109881582