Android Deep Link跳转后nav_graph_login残留回退栈问题求助
问题描述
我们的应用使用OpenId AppAuth库实现登录功能,登录相关的初始Fragment及弹窗位于nav_graph_login中。登录成功后,通过app:popUpTo=@id/nav_graph_login和app:popUpToInclusive="true"将登录相关Fragment从回退栈移除,跳转至nav_graph_main内的Dashboard,此时按返回键可直接退出应用,该逻辑运行正常。
但通过Deep Link跳转至详情Fragment时,nav_graph_login会残留于回退栈中,导致在Dashboard按返回键会回到登录页面,不符合预期。根据Android官方文档说明:
与显式深层链接一样,嵌套导航图时,每个嵌套层级的起始目标——即层级中每个
元素的起始目标——也会被添加到栈中。
推测这是问题成因,我们在MainActivity中处理Deep Link,相关代码及导航图配置如下,求解决方法?
现有代码
MainActivity代码
class MainActivity { override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) // there is more logic here, but for the sake of this post, I keep it simple navController.handleDeepLink(intent) } }
main_activity.xml
<androidx.constraintlayout.widget.ConstraintLayout 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"> <androidx.fragment.app.FragmentContainerView android:id="@+id/nav_host_fragment" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="0dp" android:layout_height="0dp" app:defaultNavHost="true" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:navGraph="@navigation/nav_graph_login" /> </androidx.constraintlayout.widget.ConstraintLayout>
nav_graph_login
<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/welcomeScreenFragment"> <include app:graph="@navigation/nav_graph_main" /> <fragment android:id="@+id/welcomeScreenFragment" android:name="my.app.WelcomeScreenFragment" android:label="WelcomeScreenFragment"> <action android:id="@id/action_dashboard" app:destination="@id/nav_graph_main" app:popUpTo="@id/nav_graph_login" app:popUpToInclusive="true" /> </fragment> </navigation>
nav_graph_main
<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_main" app:startDestination="@id/dashboard"> <action android:id="@+id/action_dashboard" app:destination="@id/nav_graph_main" app:popUpTo="@id/nav_graph" app:popUpToInclusive="true" /> <fragment android:id="@+id/dashboard" android:name="my.app.Dashboard"> <action android:id="@+id/action_dashboardFragment_to_details1Fragment" app:destination="@id/details1" /> </fragment> <fragment android:id="@+id/details1" android:name="my.app.Details1Fragment"> <deepLink android:id="@+id/deepLinkToDetails1" app:uri="a-host.net/details/{id}" /> </fragment> </navigation>
解决方案
方案一:调整导航图结构,分离登录与主流程
将登录和主流程导航图设为平级,通过根导航图管理,同时根据登录状态动态初始化导航:
- 新建根导航图
nav_graph_root,包含登录和主流程导航:
<navigation xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/nav_graph_root" app:startDestination="@id/nav_graph_login"> <include app:graph="@navigation/nav_graph_login" /> <include app:graph="@navigation/nav_graph_main" /> </navigation>
- 修改
main_activity.xml的NavHost指向根导航图:
<androidx.fragment.app.FragmentContainerView android:id="@+id/nav_host_fragment" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="0dp" android:layout_height="0dp" app:defaultNavHost="true" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:navGraph="@navigation/nav_graph_root" />
- 在
MainActivity中根据登录状态初始化导航,处理Deep Link时清理回退栈:
class MainActivity : AppCompatActivity() { private lateinit var navController: NavController override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main_activity) val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment navController = navHostFragment.navController // 替换为你的实际登录状态检查逻辑 val isLoggedIn = checkLoginStatus() if (isLoggedIn) { // 直接跳转主流程并清空回退栈 navController.navigate(R.id.nav_graph_main, null, NavOptions.Builder() .setPopUpTo(R.id.nav_graph_root, true) .build() ) } } override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) val isLoggedIn = checkLoginStatus() if (isLoggedIn) { // 处理Deep Link时确保回退栈无登录页面 val navOptions = NavOptions.Builder() .setPopUpTo(R.id.nav_graph_main, false) .build() navController.handleDeepLink(intent, navOptions) } else { // 未登录时暂存Deep Link,登录成功后再处理 navController.navigate(R.id.nav_graph_login, intent.extras) } } private fun checkLoginStatus(): Boolean { // 示例:读取本地token判断登录状态 return getSharedPreferences("auth", MODE_PRIVATE).getString("token", null) != null } }
方案二:处理Deep Link时手动清理登录栈
无需调整导航结构,在处理Deep Link前先清理回退栈中的登录相关页面:
override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) val isLoggedIn = checkLoginStatus() if (isLoggedIn) { // 先清理登录栈 val cleanStackOptions = NavOptions.Builder() .setPopUpTo(R.id.nav_graph_login, true) .build() navController.navigate(R.id.dashboard, null, cleanStackOptions) // 再处理Deep Link跳转 navController.handleDeepLink(intent) } else { navController.navigate(R.id.nav_graph_login) } }
方案三:给Deep Link配置导航选项
在nav_graph_main的Deep Link中直接添加清理登录栈的配置,注意需配合登录状态检查避免跳过登录:
<fragment android:id="@+id/details1" android:name="my.app.Details1Fragment"> <deepLink android:id="@+id/deepLinkToDetails1" app:uri="a-host.net/details/{id}" app:popUpTo="@id/nav_graph_login" app:popUpToInclusive="true"/> </fragment>
在Details1Fragment中添加登录校验:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (!checkLoginStatus()) { // 未登录则跳转登录页 findNavController().navigate(R.id.nav_graph_login) } }
内容的提问来源于stack exchange,提问作者ASP
相关产品推荐
相关产品推荐

