持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第14天,点击查看活动详情
概述
如果我们的应用会向用户提供通知或者应用微件,那么在这两种情况下,我们可能会倾向于直接跳转到应用中指定的目的地上。针对这种情况,我们可以选择创建深层链接来导航到目的地。
显式深层链接
显式深层链接是深层链接的一个实例,使用PendingIntent将用户导航到应用内指定的目的地。当用户通过显式深层链接打开应用时,任务返回堆栈会被清除,并被替换为相应的深层链接目的地。
下面的代码使用显式深层链接的方式从一个目的地跳转到另一个目的地:
val pendingIntent = NavDeepLinkBuilder(requireContext())
.setGraph(R.navigation.nav_home)
.setDestination(R.id.fragment_deep_link_second)
.setComponentName(NavStudyHomeActivity::class.java)
.createPendingIntent()
//创建一个通知
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
val channel = NotificationChannelCompat.Builder("test",NotificationManagerCompat.IMPORTANCE_DEFAULT)
.setDescription("测试相关的渠道")
.setName("test channel")
.build()
NotificationManagerCompat.from(requireContext()).createNotificationChannel(channel)
}
val notification = NotificationCompat.Builder(requireContext(),"test")
.setSmallIcon(R.drawable.ic_video_play)
.setContentTitle("点击跳转到深层链接第二个页面")
.setPriority(NotificationManagerCompat.IMPORTANCE_DEFAULT)
.setContentIntent(pendingIntent)
.build()
//显示通知
NotificationManagerCompat.from(requireContext()).notify(100,notification)
在上面的代码中,我们首先创建了一个PendingIntent,我们向其中设置了下面的属性:
setGraph: 设置了导航图的资源文件,需要注意的是,我们需要设置导航图的根资源文件,也就是和Activity绑定的资源文件,其它的资源文件都是通过<include>导入到这个根文件中的setDestination: 设置了我们要导航到的目的地setComponentName: 这个属性用于设置我们导航的目的地所在的Activity,由于我们的项目本身并不是单Activity多Fragment的项目,所以需要这个属性,如果是单Activity的项目则无需设置此属性createPendingIntent(): 通过这个方法最终创建出我们需要的PendingIntent
创建完这个PendingIntent之后,我们接下来创建了一个通知,当我们打开通知栏点击这个通知的时候,就会执行跳转到相应的目的地,下面的动图演示了上面代码的执行结果:
通过上面的动图也可以看到,我们在退出的时候并不是直接退出到桌面,而是根据导航路径一步一步返回到桌面中,这主要是我们要导航的目的地是通过<include>将目的地所在的图表嵌套到根图标文件中的,在这种情况下,每个嵌套级别的起始目的地均会被添加到堆栈中,这样对于用户来说,按返回键的时候就像是从入口点进去的应用一样。
隐式深层链接
隐式深层链接也是指向应用中特定的目的地,我们可以通过URI,intent操作以及MIME类型匹配深层链接。我们可以为单个深层链接指定多个匹配类型,但是匹配的优先顺序为URI,action和MIME类型。
我们通过在目的地标签中使用deeplink标签来指定可以匹配到的隐式深层链接,如下所示:
<deepLink
app:uri="www.example.nav"
app:action="com.example.project.nav.Second"
app:mimeType="type/subtype"
/>
在设置隐式深层链接的时候需要注意以下内容:
- 没有设置架构的
URI会被默认设置为http或者https,例如上面的www.example.nav可以匹配的URI为http://www.example.nav或者https://www.example.nav - 形式为
{placeholder_name}的路径占位符可以与一个或者多个字符相匹配,例如www.example.nav/user/{id}可以和www.example.nav/user/4匹配。 - 可以使用查询参数占位符代替路径参数,也可以将查询参数占位符和路径参数结合使用,例如
www.example.nav/user/{id}?myarg={myarg}可以和www.example.nav/user/4?myarg=10匹配 - 使用默认值或者可为空的值所定义的变量的查询参数占位符无需匹配。例如
www.example.nav/user/{id}?arg1={arg1}&arg2={arg2}可以和www.example.nav/user/4?arg1=10或者www.example.nav/user/4?arg2=20相匹配。但是路径参数并非如此,定义了路径参数则必须设置路径参数的值,否则无法匹配,比如www.example.nav/user?arg1=10&arg2=10无法和上面的模式匹配,因为没有提供所需的路径参数 - 多余的查询参数不会影响深层链接的匹配。比如
www.example.nav/user/4?arg=10可以匹配上www.example.nav/user/{id}
要启动隐式深层链接,还必须在Manifest文件中对应的Activity标签下添加nav-graph标签,如下所示:
<deepLink app:uri="test://www.example.com/user/{id}"
app:action="android.intent.action.MY_ACTION"
app:mimeType="type/subtype"/>
上面的代码中我们指定的当前这个目的地可以处理的深层链接,下面我们在另一个Activity中使用隐式启动的方式启动到这个目的地中:
val intent = Intent().apply{
this.data = Uri.parse("test://www.example.com/user/10")
}
startActivity(intent)
运行上面的代码,我们就可以启动到相应的目的地,但是在使用的过程中出现了一些的问题:
- 如果
URL没有配置路径的话则无法正确匹配到需要导航的页面 - 设置的
action并没有效果,查看安装包中的数据,相应的Activity并没有配置上面的action,这就导致无法通过上面配置的action启动到目的地,暂时不清楚是为什么 - 通过第一个
Activity隐式启动到目标目的地,返回的时候无法返回到第一个Activity(并没有设置FLAG_ACTIVITY_NEW_TASK)
在触发隐式深层链接的时候,返回堆栈的状态取决于是否使用Intent.FLAG_ACTIVITY_NEW_TASK标志:
- 如果设置了这个标志,任务返回堆栈就会被清除,并被替换为相应的深层链接目的地。与显示深层链接一样,当嵌套图表时,每个嵌套图表的起始目的地均会被加入到返回堆栈中,当用户按下返回按钮的时候,会返回到相应的导航堆栈,就像用户正常从首页进入的一样。
- 如果没有设置这个标志,我们仍然会位于触发隐式深层链接时所在的上一个应用的任务堆栈中。在这种情况下,如果按下返回键,就会返回到上一个应用中。
处理深层链接
一般情况下,我们使用单Activity多Fragment的开发模式,这个唯一的Activity就是MainActivity,我们不需要去设置它的启动模式,但是有时候出于一些特殊情况我们可能会设置launchModel属性,如果我们的启动模式就是standard,那么不需要单独处理。如果我们设置了启动模式为singleTop或者singleTask,那么当我们的页面回调之后我们就需要处理onNewIntent()中的操作:
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
navController.handleDeepLink(intent)
}