Android 应用链接

通常需要连接网站和移动应用,以便网站上的链接启动该移动应用并在移动应用中显示内容。 应用链接(也称为深层链接)技术使移动设备能够响应 URI 并在由 URI 表示的移动应用中启动内容。

Android 通过意向系统处理应用链接。 在移动浏览器中点击链接时,浏览器会调度 Android 将要委托给已注册应用的意向。 这些链接可以基于自定义方案(例如 myappname://),也可以使用 HTTP 或 HTTPS 方案。 例如,单击食谱网站上的链接将打开与该网站关联的移动应用,然后向用户显示特定食谱。 如果注册了多个应用来处理意向,Android 将显示一个消除歧义对话框,询问用户选择哪个应用来处理意向。 未安装应用的用户将转到网站上的内容。

Android 将应用链接分为三个类别:

  • 深层链接是任何将用户转到应用中特定内容的方案的 URI。 单击深层链接时,可能会显示一个消除歧义对话框,要求用户选择一个应用来处理深层链接。
  • Web 链接是使用 HTTP 或 HTTPS 方案的深层链接。 在 Android 12 及更高版本上,Web 链接始终在 Web 浏览器中显示内容。 在早期版本的 Android 上,如果应用可以处理 Web 链接,则会显示一个消除歧义对话框,要求用户选择一个应用来处理 Web 链接。
  • Android 应用链接可在 API 23+ 上使用,它们是使用 HTTP 或 HTTPS 方案的 Web 链接,其中包含 autoVerify 属性。 此属性使你的应用成为应用链接的默认处理程序。 因此单击应用链接时将打开应用,且不显示消除歧义对话框。

.NET MAUI Android 应用可以支持全部三类应用链接。 但是,本文重点介绍 Android 应用链接。 这需要证明域的所有权,并在该域上托管数字资产链接文件 JSON 文件,该文件描述与应用的关系。 这使 Android 能够验证试图处理 URI 的应用是否拥有 URI 域的所有权,以防止恶意应用截获应用链接。

在 .NET MAUI Android 应用中处理 Android 应用链接的过程如下所示:

  1. 验证域所有权。 有关详细信息,请参阅验证域所有权
  2. 在网站上创建并托管数字资产链接文件。 有关详细信息,请参阅创建并托管数字资产链接文件
  3. 在应用中为网站 URI 配置意向筛选器。 有关详细信息,请参阅配置意向筛选器
  4. 读取传入意向的数据。 有关详细信息,请参阅读取传入意向的数据

重要

若要使用 Android 应用链接:

  • 你的应用必须在 Google Play 上发布一个版本。
  • 必须在 Google 开发人员控制台中针对该应用注册配套网站。 应用与网站关联后,可以编制适用于该网站和应用 URI 的索引,然后可在搜索结果中提供这些 URI。 有关详细信息,请参阅 support.google.com 上的 google Search 上的应用索引

有关 Android 应用链接的详细信息,请参阅处理 Android 应用链接

验证域所有权

你需要验证在 Google 搜索控制台中提供应用链接的域的所有权。 所有权验是指证明你拥有特定网站。 Google 搜索控制台支持多种验证方法。 有关详细信息,请参阅 support.google.com 上的验证网站所有权

Android 应用链接需要 Android 在将应用设置为 URI 的默认处理程序之前验证该应用与网站之间的关联。 首次安装应用时将进行此验证。 数字资产链接文件是一个 JSON 文件,必须由位于以下位置的相关 Web 域托管:https://domain.name/.well-known/assetlinks.json

数字资产文件包含 Android 验证关联所需的元数据。 该文件需要以下键值对:

  • namespace - Android 应用的命名空间。
  • package_name - Android 应用的包名称。
  • sha256_cert_fingerprints - 从 .keystore 文件获取的已签名应用的 SHA256 指纹。 有关查找密钥存储的签名的信息,请参阅查找密钥存储的签名

以下示例 assetlinks.json 文件向 com.companyname.myrecipeapp Android 应用授予链接打开权限:

[
   {
      "relation": [
         "delegate_permission/common.handle_all_urls"
      ],
      "target": {
         "namespace": "android_app",
         "package_name": "com.companyname.myrecipeapp",
         "sha256_cert_fingerprints": [
            "14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"
         ]
      }
   }
]

可以注册多个 SHA256 指纹以支持应用的不同版本。 以下 assetlinks.json 文件向 com.companyname.myrecipeappcom.companyname.mycookingapp Android 应用授予链接打开权限:

[
   {
      "relation": [
         "delegate_permission/common.handle_all_urls"
      ],
      "target": {
         "namespace": "android_app",
         "package_name": "com.companyname.myrecipeapp",
         "sha256_cert_fingerprints": [
            "14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"
         ]
      }
   },
   {
      "relation": [
         "delegate_permission/common.handle_all_urls"
      ],
      "target": {
         "namespace": "android_app",
         "package_name": "com.companyname.mycookingapp",
         "sha256_cert_fingerprints": [
            "14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"
         ]
      }
   }
]

提示

使用语句列表生成器和测试器工具来帮助生成正确的 JSON 并对其进行验证。

将 JSON 验证文件发布到 https://domain.name/.well-known/assetlinks.json 时,必须确保:

  • 该文件的内容类型为 application/json
  • 无论应用是否使用 HTTPS 方案,该文件都必须可通过 HTTPS 访问。
  • 必须无需重定向即可访问该文件。
  • 如果应用链接支持多个域,则必须在每个域上发布 assetlinks.json 文件。

可以使用 Google 的数字资产链接 API 来确认数字资产文件的格式和托管是否正确:

https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=
  https://<WEB SITE ADDRESS>:&relation=delegate_permission/common.handle_all_urls

有关详细信息,请参阅 developer.android.com 上的声明网站关联

配置意向筛选器

必须配置意向筛选器,以便将 URI 或 URI 集从网站映射到 Android 应用中的活动。 在 .NET MAUI 中,可以通过向活动添加 IntentFilterAttribute 来实现此目的。 意向筛选器必须声明以下信息:

  • ActionView - 这将注册意向筛选器以响应查看信息的请求。
  • Categories - 意向筛选器应同时注册 CategoryDefaultCategoryBrowsable,从而正确处理 Web URI。
  • DataScheme - 意向筛选器必须声明自定义方案和/或 HTTPS。
  • DataHost - 这是 URI 源自的域。
  • DataPathPrefix - 这是网站上的资源的可选路径,必须以 / 开头。
  • AutoVerify - 这会告知 Android 以验证应用与网站之间的关系。 它必须设置为 true,否则 Android 不会验证该应用与网站之间的关联,因此不会将你的应用设置为 URI 的默认处理程序。

以下示例演示如何使用 IntentFilterAttribute 处理 https://www.recipe-app.com/recipes 的链接:

using Android.App;
using Android.Content;
using Android.Content.PM;

namespace MyNamespace;

[Activity(
    Theme = "@style/Maui.SplashTheme",
    MainLauncher = true,
    ConfigurationChanges = ConfigChanges.ScreenSize |
        ConfigChanges.Orientation |
        ConfigChanges.UiMode |
        ConfigChanges.ScreenLayout |
        ConfigChanges.SmallestScreenSize |
        ConfigChanges.KeyboardHidden |
        ConfigChanges.Density)]
[IntentFilter(
    new string[] { Intent.ActionView },
    Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable },
    DataScheme = "https",
    DataHost = "recipe-app.com",
    DataPath = "/recipe",
    AutoVerify = true,)]    
public class MainActivity : MauiAppCompatActivity
{
}

注意

可以在意向筛选器中指定多个方案和主机。 有关详细信息,请参阅 developer.android.com 上的 创建指向应用内容的深层链接

在将应用注册为 URI 的默认处理程序之前,Android 会根据网站上的数字资产文件验证意向筛选器中标识的每个主机。 所有意向筛选器都必须通过验证,然后 Android 才能将应用建立为默认处理程序。 添加包含活动内容的 URI 的意向筛选器后,Android 就能够在运行时将任何具有匹配 URI 的意向路由到你的应用。

可能还需要将活动标记为可导出,使其他应用可以启动活动。 可以通过将 Exported = true 添加到现有的 ActivityAttribute 项来实现此目的。 有关详细信息,请参阅 developer.android.com 上的 活动元素

调用 Web URI 意向时,Android 会尝试以下操作,直到请求成功:

  1. 打开用于处理 URI 的首选应用。
  2. 打开唯一可处理 URI 的应用。
  3. 允许用户选择应用来处理 URI。

有关意向和意向筛选器的详细信息,请参阅 developer.android.com 上的意向和意向筛选器

读取传入意向的数据

当 Android 通过意向筛选器启动活动时,可以使用意向提供的数据来确定要执行的操作。 这应该在早期生命周期委托中执行(最好是 OnCreate)。 创建活动时将调用 OnCreate 委托。 有关生命周期委托的详细信息,请参阅平台生命周期事件

若要响应要调用的 Android 生命周期委托,请在 MauiProgram 类的 CreateMauiapp 方法中在 MauiAppBuilder 对象上调用 ConfigureLifecycleEvents 方法。 然后,在 ILifecycleBuilder 对象上,调用 AddAndroid 方法并指定为所需委托注册处理程序的 Action

using Microsoft.Maui.LifecycleEvents;
using Microsoft.Extensions.Logging;

namespace MyNamespace;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            })
            .ConfigureLifecycleEvents(lifecycle =>
            {
#if ANDROID
                lifecycle.AddAndroid(android =>
                {
                    android.OnCreate((activity, bundle) =>
                    {
                        var action = activity.Intent?.Action;
                        var data = activity.Intent?.Data?.ToString();

                        if (action == Android.Content.Intent.ActionView && data is not null)
                        {
                            Task.Run(() => HandleAppLink(data));
                        }
                    });
                });
#endif
            });

#if DEBUG
        builder.Logging.AddDebug();
#endif

        return builder.Build();
    }

    static void HandleAppLink(string url)
    {
        if (Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out var uri))
            App.Current?.SendOnAppLinkRequestReceived(uri);
    }
}

Intent.Action 属性检索与传入意向关联的操作,而 Intent.Data 属性检索与传入意向关联的数据。 如果意向操作设置为 ActionView,可以使用 SendOnAppLinkRequestReceived 方法将意向数据传递给 App 类。

警告

应用链接为应用提供潜在的攻击途径,因此请确保验证所有 URI 参数并丢弃任何格式不正确的 URI。

App 类中,替代用于接收和处理意向数据的 OnAppLinkRequestReceived 方法:

namespace MyNamespace;

public partial class App : Application
{
    public App()
    {
        InitializeComponent();

        MainPage = new AppShell();
    }

    protected override async void OnAppLinkRequestReceived(Uri uri)
    {
        base.OnAppLinkRequestReceived(uri);

        // Show an alert to test that the app link was received.
        await Dispatcher.DispatchAsync(async () =>
        {
            await Windows[0].Page!.DisplayAlert("App link received", uri.ToString(), "OK");
        });

        Console.WriteLine("App link: " + uri.ToString());
    }
}

在上面的示例中,OnAppLinkRequestReceived 替代会显示应用链接 URI。 在实践中,应用链接应将用户直接转到 URI 表示的内容,而无需任何提示、登录或其他中断。 因此,OnAppLinkRequestReceived 替代是从中调用导航到 URI 所表示的内容的位置。

如果数字资产文件已正确托管,则可以使用 Android Debug Bridge (adb) 和活动管理器工具 (am) 模拟打开 URI,以确保应用链接正常工作。 例如,以下命令试图查看与 URI 关联的目标应用活动:

adb shell am start -W -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d YOUR_URI_HERE

此命令将调度应由 Android 定向到移动应用的意向,该应用应启动并显示为 URI 注册的活动。

注意

可以在模拟器或设备上运行 adb

此外,还可以显示设备上安装的应用的现有链接处理策略:

adb shell dumpsys package domain-preferred-apps

此命令将显示以下信息:

  • 包 - 应用的包名称。
  • 域 - 由空格分隔的域,其 Web 链接将由应用处理。
  • 状态 - 应用当前的链接处理状态。 always 值表示应用已将 AutoVerify 设置为 true 并通过系统验证。 后面是一个十六进制数字,代表偏好的记录。

有关 adb 命令的详细信息,请参阅 Android Debug Bridge

此外,还可以通过 Play 管理中心来管理和验证 Android 应用链接。 有关详细信息,请参阅 developer.android.com 上的管理和验证 Android 应用链接

有关故障排除建议,请参阅 developer.android.com 上的修复常见实现错误