如何在onNewIntent中重置Android Navigation至初始状态?
我有一个用于接收YouTube分享URL的导航流程:
ScanFragment -> PlaylistFragment -> VideoFragment
ScanFragment负责解析URL并加载数据,之后跳转至对应的Fragment展示内容。
当用户在VideoFragment或PlaylistFragment界面按下Home键后,Activity仍保持存活状态,此时导航不在初始的ScanFragment位置。当用户分享另一个URL时,Intent会通过onNewIntent传递过来,此时需要重置导航,让URL能被路由至ScanFragment。
我曾尝试手动跳转至ScanFragment,但存在延迟且返回栈状态异常。导航是通过NavHostFragment(app:navGraph)初始化的,如何在onNewIntent中强制重置导航?
补充配置信息
我已为ScanFragment添加了DeepLink配置,导航图代码如下:
<fragment android:id="@+id/share_navigation_scan" android:name="uk.co.sentinelweb.cuer.app.ui.share.scan.ScanFragment" android:label="@string/sharenav_title_scan" tools:layout="@layout/fragment_scan"> <action android:id="@+id/action_goto_playlist" app:destination="@id/share_navigation_playlist" app:launchSingleTop="true" app:popUpTo="@id/navigation_playlist" app:popUpToInclusive="true" /> <action android:id="@+id/action_goto_playlist_item" app:destination="@id/share_navigation_playlist_item_edit" app:launchSingleTop="true" app:popUpTo="@id/navigation_playlist_item_edit" app:popUpToInclusive="true" /> <deepLink app:uri="https://www.youtube.com/watch?v={id}" /> <deepLink app:uri="https://youtube.com/watch?v={id}" /> <deepLink app:uri="https://m.youtube.com/watch?v={id}" /> <deepLink app:uri="https://youtu.be/{id}" /> <deepLink app:uri="https://www.youtube.com/playlist?list={id}" /> <deepLink app:uri="https://youtube.com/playlist?list={id}" /> </fragment>
同时移除了android:launchMode="standard",Activity清单配置如下:
<activity android:name=".app.ui.share.ShareActivity" android:exported="true"> <nav-graph android:value="@navigation/deeplink" /> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> <data android:scheme="http" /> <data android:scheme="https" /> <data android:host="youtu.be" /> <data android:pathPattern="/" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> <data android:host="youtu.be" /> <data android:path="/" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> <data android:host="youtube.com" /> <data android:host="www.youtube.com" /> <data android:host="m.youtube.com" /> <data android:path="/watch" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> <data android:host="youtube.com" /> <data android:host="www.youtube.com" /> <data android:host="m.youtube.com" /> <data android:path="/playlist" /> </intent-filter> </activity>
当前onNewIntent代码:
override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) navController.handleDeepLink(intent) checkIntent(intent) }
checkIntent会尝试从Intent中获取URL并传递给ScanFragment,但调用时当前Fragment还不是ScanFragment。尝试移除checkIntent,在ScanFragment的onStart中获取URL也无效,调用handleDeepLink后导航仍无法回到ScanFragment。
1. 修正DeepLink的导航栈重置逻辑
你的DeepLink配置未指定清空返回栈的规则,导致触发DeepLink时不会重置现有导航栈。需要给每个deepLink标签添加app:popUpTo和app:popUpToInclusive属性,强制导航回到ScanFragment并清空之前的栈内容:
<deepLink app:uri="https://www.youtube.com/watch?v={id}" app:popUpTo="@id/share_navigation_scan" app:popUpToInclusive="true" /> <!-- 其余所有deepLink标签都需要添加上述两个属性 -->
2. 配置Activity启动模式并修正onNewIntent逻辑
- 先给Activity设置
singleTop启动模式,确保Activity存活时收到新Intent会调用onNewIntent而不是创建新实例:
<activity android:name=".app.ui.share.ShareActivity" android:exported="true" android:launchMode="singleTop"> <!-- 其余配置保持不变 --> </activity>
- 修改
onNewIntent代码,先更新Activity的Intent为最新值,再处理DeepLink,确保后续获取的Intent是最新的分享请求:
override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) setIntent(intent) // 更新Activity的Intent为最新 navController.handleDeepLink(intent) // 触发DeepLink导航 }
3. 在ScanFragment中统一处理URL解析
移除checkIntent方法,将URL解析逻辑移到ScanFragment中,在onViewCreated和onResume中处理最新Intent:
class ScanFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) processIntent(requireActivity().intent) } override fun onResume() { super.onResume() // 每次回到前台时重新检查Intent,确保处理最新的分享URL processIntent(requireActivity().intent) } private fun processIntent(intent: Intent) { when { intent.action == Intent.ACTION_SEND && intent.type == "text/plain" -> { intent.getStringExtra(Intent.EXTRA_TEXT)?.let { parseYoutubeUrl(it) } } intent.action == Intent.ACTION_VIEW -> { intent.data?.toString()?.let { parseYoutubeUrl(it) } } } } private fun parseYoutubeUrl(url: String) { // 根据URL类型跳转到对应Fragment val actionId = if (url.contains("playlist")) { R.id.action_goto_playlist } else { R.id.action_goto_playlist_item } findNavController().navigate(actionId) } }
4. 修正Fragment跳转的返回栈行为
当前跳转到其他Fragment的action配置中,popUpTo目标错误(比如跳转到PlaylistFragment的popUpTo设成了navigation_playlist),需要修正为跳转到ScanFragment,确保返回逻辑正确:
<action android:id="@+id/action_goto_playlist" app:destination="@id/share_navigation_playlist" app:launchSingleTop="true" app:popUpTo="@id/share_navigation_scan" app:popUpToInclusive="false" />
这样从PlaylistFragment返回时,会回到ScanFragment而不是直接退出Activity。
内容的提问来源于stack exchange,提问作者siliconeagle

