使用深层链接导航 | MAD Skills

使用深层链接导航 | MAD Skills

这是一个新的系列文章,我们称之为 "Modern Android Development 技巧",简称为 "MAD Skills"。本系列文章致力于帮助开发者们打造更好的现代 Android 开发体验,敬请关注。

今天为大家发布本系列文章中的第四篇: 使用深层链接 (Deep Links) 导航。如果您想回顾过去发布的内容,请参考下面链接查看:

介绍

这篇文章的内容是关于 深层链接 的,导航 (Navigation) 组件提供了该功能以帮助用户从应用的外部到达应用的深层页面。

有时您会想让用户更便捷地到达应用流程中指定的页面,而不必从初始页一直不断地点击来跳转到那个页面。比如在聊天应用中直接显示正在进行的对话详情页,再比如购物应用中用户的购物车详情页。您可以使用深层链接来实现上述需求,通过点击类似快捷方式和通知等应用的外部链接来到达您应用的深层页面。

导航组件简化了这些深层链接的创建步骤。为了展示其工作原理,我们将再次回顾我在 以前文章 中使用的甜甜圈记录应用。该应用的 完整代码 发布在 Github 上,请下载并使用 Android Studio 打开。

代码已经完成,所以我将会逐步解释如何利用导航组件来实现深层链接。

甜甜圈深层链接

在甜甜圈记录应用中,有几个操作如果能快速到达会非常方便。比如我突然发现一个非常不错的甜甜圈,并希望能快速地将其信息记录到列表中,但是我并不想启动应用,再点击 FloatingActionButton 按钮来弹出数据输入对话框。再比如我正在新增或者编辑一个甜甜圈的信息,希望应用能够发送一个通知,这样我就能快速地编辑最近的这个条目。

我为这两个操作都添加了深层链接,一个是为了添加新的甜甜圈,而另一个是为了返回正在编辑的条目。这个 "新增" 操作使用的是 "隐式" 深层链接,隐式意味着这个深层链接会带用户到您应用层次结构中的一个固定页面,这个位置也不会随着时间而改变。在我的应用中,该隐式深层链接会一直带您到允许在列表中添加新甜甜圈的表单页。

"继续编辑" 操作使用的是 "显式" 深层链接,显式的意思是我们调用这个深层链接可以带用户到您应用中的一个动态页面。

创建隐式深层链接

我们来先创建新增甜甜圈表单页的隐式深层链接。

首先,我需要使用导航编辑器来创建这个深层链接。点击对话框目的地可以在右边显示这个目的地页面的属性:

点击目的地可以显示其属性,我们可以在属性栏中创建一个新的深层链接来导航到这个目的地

点击目的地可以显示其属性,我们可以在属性栏中创建一个新的深层链接来导航到这个目的地

点击 Deep Links (深层链接) 旁边的 + 来打开一个对话框,在这个对话框中,我输入了一个 URI (Universal Resource Identifier)。这里我们需要一个应用特有的 URI (而不能是一个通用的 web 地址,不然它会触发浏览器应用),所以我使用了我们应用特有的 "myapp" 作为标识符:

创建一个新的深层链接会打开一个对话框,您可以在这输入一个跳转到该目的地的深层链接 URI

创建一个新的深层链接会打开一个对话框,您可以在这输入一个跳转到该目的地的深层链接 URI

接下来,我通过编辑应用的 manifest 文件来通知应用为深层链接创建一个快捷方式:

<activity
    android:name="com.android.samples.donuttracker.MainActivity"
    android:label="@string/app_name"
    android:theme="@style/AppTheme.NoActionBar">
  <nav-graph android:value="@navigation/nav_graph" />
   <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
   <meta-data android:name="android.app.shortcuts"
        android:resource="@xml/shortcuts" />
</activity>

在 meta-data 代码块中,我告诉 activity 关于该深层链接在导航图中的信息。同时我也引用了一个 xml 资源文件夹中的新文件,这个文件包含了到达该 activity 应用快捷方式的信息。

然后我又创建了 xml 文件夹,并且在其中新创建了一个 shortcuts.xml 文件。在这个快捷方式文件中,我输入了关于这个应用快捷方式的信息,包括上面我们看到的 URI:

<shortcuts
    xmlns:android="http://schemas.android.com/apk/res/android">
  <shortcut
    android:shortcutId="NewDonutDialogFragment"
    android:enabled="true"
    android:shortcutShortLabel="@string/static_shortcut_label_short"
    android:shortcutLongLabel="@string/static_shortcut_label_long"
    android:shortcutDisabledMessage=
        "@string/static_shortcut_disabled_message"
    android:icon="@drawable/donut_with_sprinkles">
    <intent
       android:action="android.intent.action.VIEW"
      android:data="myapp://navdonutcreator.com/donutcreator" />
 </shortcut>
</shortcuts>

最重要的部分是 data 字段,它的值是在之前导航工具深层链接对话框中输入的 URI,这也是从应用快捷方式导航到对话框目的地的纽带。

以上内容就是创建一个隐式深层链接所需的操作,我声明了要导航的目的地,并且创建了一个快捷方式来实现导航。如果运行这个应用并长按这个应用的图标,您会看到这个快捷方式,点击这个快捷方式会带您到可以创建新甜甜圈的表单页。

上面介绍的是隐式深层链接,接下来我们来创建一个显式深层链接,这个链接会根据应用的状态动态地被创建。

创建显式深层链接

如果您像我一样,甜甜圈在您的生活里也非常重要,因此当我输入一个新发现的甜甜圈信息时,可能想慢慢来,我可能会先输入一部分信息,当我有机会享用后再回来慢慢补充其他剩余信息。

这可以使用通知来实现,当我输入一个甜甜圈信息的时候,应用会创建一个通知以便之后可以快捷地返回正在编辑的条目。这其实不需要太多的代码来实现,只要创建一个带 PendingIntent 的通知,就可以带我们返回应用的正确页面。

上面的逻辑大部分发生在 DonutEntryDialogFragment 中,在 Done 按钮的 onClick() 监听器回调函数中。我们已经在 之前的文章 中介绍过这个点击监听器的代码,我们可以在这里新增和更新 ViewModel 的数据。我只需要再添加创建通知这一步,如下代码所示:

binding.doneButton.setOnClickListener {
    // 先获取上下文参数,因为 Fragment 可能在下面的 lambda 调用前就消失了
    val context = requireContext().applicationContext
    val navController = findNavController()

    donutEntryViewModel.addData(
        donut?.id ?: 0,
        binding.name.text.toString(),
        binding.description.text.toString(),
        binding.ratingBar.rating.toInt()
    ) { actualId ->
        val arg = DonutEntryDialogFragmentArgs(actualId).toBundle()
        val pendingIntent = navController
            .createDeepLink()
            .setDestination(R.id.*donutEntryDialogFragment*)
            .setArguments(arg)
            .createPendingIntent()

        Notifier.postNotification(actualId, context, pendingIntent)
    }
    dismiss()
}

首先,代码创建了带甜甜圈 id 的参数 arg,可以用这个参数来告诉目的地对话框应该获取哪个甜甜圈信息,来让用户继续编辑。

代码使用了一个 NavigationController 的 API 为深层链接创建 pendingIntent,其目的地被设置为对话框 fragment,这个调用既设置了包括甜甜圈 ID 的参数也创建了 intent。

代码还调用了 Notifer.postNotification(),这是我为了处理通知的创建和发送操作而增加的一个工具类方法。

fun postNotification(id: Long, 
                     context: Context, 
                     intent: PendingIntent) {
    val builder = NotificationCompat.Builder(context, channelId)
    builder.setContentTitle(context
            .getString(R.string.deepLinkNotificationTitle))
        .setSmallIcon(R.drawable.donut_with_sprinkles)
    val text = context.getString(R.string.addDonutInfo)
    val notification = builder.setContentText(text)
        .setPriority(NotificationCompat.PRIORITY_HIGH)
        .setContentIntent(intent)
        .setAutoCancel(true)
        .build()
    val notificationManager =
        NotificationManagerCompat.from(context)
    notificationManager.cancelAll()
    notificationManager.notify(id.toInt(), notification)
}

首先,这段代码会创建一个 notification builder,而初始化这个 builder 需要一个 channelId,这一初始化过程需要放在 Notifierinit() 方法中 (想了解这些细节,请查阅代码,这也是标准做法)。

接下来,我设置了通知所需要的其他数据,同时设置了 intent,然后构建这个通知。在发送这个通知之前,已存在的通知会被取消 (因为我只想编辑最近的甜甜圈)。

最后,新创建的通知被发送出来,大功告成。现在每一个新编辑 (无论是创建新甜甜圈,还是编辑已存在的甜甜圈) 都会生成一个通知,之后用户可以点击这个通知,回到这个正在编辑的操作中。

总结

在这篇文章中,我先是创建了一个隐式深层链接,这个链接可以带用户进入应用的一个固定页面,用户可以在这个页面输入新甜甜圈的信息。接着,我创建了一个显式深层链接,这个链接可以帮助用户返回其之前没有完成的甜甜圈编辑页面。

甜甜圈记录应用变得越来越好了,不过当然还是不如甜甜圈好,因为什么都不如甜甜圈。这次就到这了,去找个甜甜圈享用吧,这是您应得的。

更多信息

更多关于导航组件的详情,请查看导航组件使用 入门文档

DonutTracker 应用的完整代码,请查看 Github 示例

更多现代 Android 开发技巧 (MAD Skills) 系列内容,请查看 Android Developers 频道

感谢各位中国 Android 开发者朋友们的一路相伴,我们一直以来都非常重视您的声音,大家的想法和建议我们也都采纳并实施在产品和日常工作中。我们邀请您扫描下方二维码,向 "Android 开发者" 公众号分享您的建议和对我们的期待。

版权声明

禁止一切形式的转载-禁止商用-禁止衍生 申请授权

脉脉不得语
脉脉不得语
Zhengzhou Website
Android Developer | https://androiddevtools.cn and https://androidweekly.io WebMaster | GDG Zhengzhou Funder & Ex Organizer | http://Toast.show(∞) Podcast Host

你已经成功订阅到 Android 开发技术周报
太棒了!接下来,完成检验以获得全部访问权限 Android 开发技术周报
欢迎回来!你已经成功登录了。
Unable to sign you in. Please try again.
成功!您的帐户已完全激活,您现在可以访问所有内容。
Error! Stripe checkout failed.
Success! Your billing info is updated.
Error! Billing info update failed.