网络 intent 解析

从 Android 12 开始,仅当您的应用获准处理某个通用网络 intent 中包含的特定网域时,该网络 intent 才会解析为应用中的 activity。如果您的应用未获准处理相应的网域,则该网络 intent 会解析为用户的默认浏览器应用。

应用可通过执行以下某项操作来获准处理相应的网域:

如果您的应用调用网络 intent,不妨考虑添加一条提示或一个对话框,要求用户确认操作。

网络 intent 属性

网络 intent 是包含以下特征的 intent

网络 intent 过滤器必须包含可浏览的类别

从 Android 12 开始,为使给定的网络 intent 解析为应用中的 activity,网络 intent 过滤器还必须声明 CATEGORY_BROWSABLE 类别。

Android 8.0(API 级别 26)及更高版本中的 Android App Links 使用 Digital Asset Links API 将特定应用与网域相关联。这种关联可让系统相信应用已获得网站的批准,可以自动打开该网域的链接。

从 Android 12 开始,您可以手动调用网域验证,以评估系统如何解析 Android App Links。

对于以 Android 12 为目标平台的应用,系统对 Android App Links 的验证方式进行了一些更改。这些更改会提升应用链接体验的可靠性,并且可让应用开发者和最终用户更好地进行控制。您可以手动调用网域验证来测试声明的可靠性。

如果以 Android 12 为目标平台并且依靠 Android App Links 验证在您的应用中打开网页链接,请更新 Android App Links 声明,以支持更改后的验证流程。

更新 Android App Links 的声明

网域验证流程需要连接到互联网,并且可能需要一些时间才能完成。为了帮助提高该流程的效率,仅当某个网域在专门设置了格式的 <intent-filter> 元素内时,系统才会为以 Android 12 为目标平台的应用验证该网域。<intent-filter> 元素必须包含以下代码段中所示的操作、类别和架构:

<!-- Make sure you explicitly set android:autoVerify to "true". -->
<intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.BROWSABLE" />
    <category android:name="android.intent.category.DEFAULT" />

    <!-- If a user clicks on a shared link that uses the "http" scheme, your
         app should be able to delegate that traffic to "https". -->
    <data android:scheme="http" />
    <data android:scheme="https" />

    <!-- Include one or more domains that should be verified. -->
    <data android:host="..." />
</intent-filter>

手动调用网域验证

从 Android 12 开始,您可以为安装在设备上的应用手动调用网域验证。无论您的应用是否以 Android 12 为目标平台,您都可以执行此流程。

建立互联网连接

如需执行网域验证,您的测试设备必须连接到互联网。

支持更新后的网域验证流程

如果您的应用以 Android 12 为目标平台,则系统会自动使用更新后的网域验证流程。

否则,您可以手动启用更新后的验证流程。为此,请在终端窗口中运行以下命令:

adb shell am compat enable 175408749 PACKAGE_NAME

在设备上重置 Android App Links 的状态

在设备上手动调用网域验证之前,您必须在测试设备上重置 Android App Links 的状态。为此,请在终端窗口中运行以下命令:

adb shell pm set-app-links --package PACKAGE_NAME 0 all

此命令会将设备置于它在用户为任何网域选择默认应用之前所处的同一状态。

调用网域验证流程

在设备上重置 Android App Links 的状态之后,您可以执行验证本身。为此,请在终端窗口中运行以下命令:

adb shell pm verify-app-links --re-verify PACKAGE_NAME

查看验证结果

稍等片刻,在验证代理完成其请求之后,查看验证结果。为此,请运行以下命令:

adb shell pm get-app-links PACKAGE_NAME

此命令的输出类似于以下内容:

com.example.pkg:
    ID: 01234567-89ab-cdef-0123-456789abcdef
    Signatures: [***]
    Domain verification state:
      example.com: verified
      sub.example.com: legacy_failure
      example.net: verified
      example.org: 1026

成功通过验证的网域的网域验证状态为 verified。其他任何状态都表示无法执行网域验证。特别是,状态为 none 表示验证代理可能尚未完成验证流程。

以下列表显示了网域验证可能会为给定的网域返回的值:

none
没有为此网域记录任何内容。再等待几分钟,以便验证代理完成与网域验证相关的请求,然后再次调用网域验证流程
verified
为发出声明的应用成功验证了网域。
approved
强行批准了网域,通常是通过执行 shell 命令来实现的。
denied
强行拒绝了网域,通常是通过执行 shell 命令来实现的。
migrated
系统保留了使用旧版网域验证的某个先前流程的结果。
restored
在用户执行数据恢复之后批准了网域。系统假定网域之前已经过验证。
legacy_failure
旧版验证程序拒绝了网域。具体的失败原因未知。
system_configured
网域已通过设备配置自动批准。
错误代码为 1024 或更大

设备验证程序专用的自定义错误代码。

请仔细检查是否已建立网络连接,然后再次调用网域验证流程

请求用户将您的应用与网域相关联

如需使您的应用获准处理某个网域,另一种方法是让用户将您的应用与该网域相关联。

检查您的应用是否已获准处理网域

在提示用户之前,请检查您的应用是否是您在 <intent-filter> 元素中定义的网域的默认处理程序。您可以使用以下某种方法来查询批准状态:

DomainVerificationManager

以下代码段演示了如何使用 DomainVerificationManager API:

Kotlin

val context: Context = TODO("Your activity or fragment's Context")
val manager = context.getSystemService(DomainVerificationManager::class)
val userState = manager.getDomainVerificationUserState(context.packageName)

// Domains that have passed Android App Links verification.
val verifiedDomains = userState.hostToStateMap
    .filterValues { it == DomainVerificationUserState.DOMAIN_STATE_VERIFIED }

// Domains that haven't passed Android App Links verification but that the user
// has associated with an app.
val selectedDomains = userState.hostToStateMap
    .filterValues { it == DomainVerificationUserState.DOMAIN_STATE_SELECTED }

// All other domains.
val unapprovedDomains = userState.hostToStateMap
    .filterValues { it == DomainVerificationUserState.DOMAIN_STATE_NONE }

Java

Context context = TODO("Your activity or fragment's Context");
DomainVerificationManager manager =
        context.getSystemService(DomainVerificationManager.class);
DomainVerificationUserState userState =
        manager.getDomainVerificationUserState(context.getPackageName());

Map<String, Integer> hostToStateMap = userState.getHostToStateMap();
List<String> verifiedDomains = new ArrayList<>();
List<String> selectedDomains = new ArrayList<>();
List<String> unapprovedDomains = new ArrayList<>();
for (String key : hostToStateMap.keySet()) {
    Integer stateValue = hostToStateMap.get(key);
    if (stateValue == DomainVerificationUserState.DOMAIN_STATE_VERIFIED) {
        // Domain has passed Android App Links verification.
        verifiedDomains.add(key);
    } else if (stateValue == DomainVerificationUserState.DOMAIN_STATE_SELECTED) {
        // Domain hasn't passed Android App Links verification, but the user has
        // associated it with an app.
        selectedDomains.add(key);
    } else {
        // All other domains.
        unapprovedDomains.add(key);
    }
}

命令行程序

在开发过程中测试您的应用时,您可以运行以下命令来查询贵单位拥有的网域的验证状态:

adb shell pm get-app-links --user cur PACKAGE_NAME

在以下示例输出中,虽然应用针对“example.org”网域的验证失败,但用户 0 已在系统设置中手动批准应用,并且没有其他软件包针对该网域进行验证。

com.example.pkg:
ID: ***
Signatures: [***]
Domain verification state:
  example.com: verified
  example.net: verified
  example.org: 1026
User 0:
  Verification link handling allowed: true
  Selection state:
    Enabled:
      example.org
    Disabled:
      example.com
      example.net

提供请求的上下文

在您发出网域批准请求之前,请先为用户提供一些上下文。例如,您可以向用户显示启动画面、对话框或类似的界面元素,向用户说明为什么您的应用应该是某个特定网域的默认处理程序。

发出请求

在用户了解您的应用需要他们执行的操作之后,发出请求。为此,请调用一个 intent,其中包含 ACTION_APP_OPEN_BY_DEFAULT_SETTINGS intent 操作,以及与目标应用的 package:com.example.pkg 匹配的数据字符串,如以下代码段所示:

Kotlin

val context: Context = TODO("Your activity or fragment's Context")
val intent = Intent(Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS,
    Uri.parse("package:${context.packageName}"))
context.startActivity(intent)

Java

Context context = TODO("Your activity or fragment's Context");
Intent intent = new Intent(Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS,
    Uri.parse("package: " + context.getPackageName()));
context.startActivity(intent);

调用该 intent 时,用户会看到一个名为默认打开的设置屏幕。此屏幕包含一个名为打开支持的链接的单选按钮,如图 1 所示。

当用户开启打开支持的链接时,一个名为要在此应用中打开的链接的部分下会出现一组复选框。在此处,用户可以选择想要与您的应用关联的网域。他们还可以选择添加链接以添加网域,如图 2 所示。当用户稍后选择他们所添加网域中的任何链接时,该链接会自动在您的应用中打开。

启用该单选按钮后,底部附近的一个部分会包含复选框以及一个名为“添加链接”的按钮
图 1. 系统设置屏幕,用户可以在该屏幕中选择默认情况下在您的应用中打开的链接。
每个复选框代表您可以添加的一个网域。该对话框的按钮包括“取消”和“添加”。
图 2. 用户可以在该对话框中选择要与您的应用关联的其他网域。

手动切换网域批准

网域验证和用户选择均可使用 shell 命令进行切换,这样可让开发者手动设置适当的状态,以便进行调试、运行测试或应对像未连接到互联网这样的情况。

adb shell pm 的输出提供了这些命令的完整说明。

请注意,即使通过 shell 命令批准,所有相同验证的优先顺序和单一批准限制也适用,所以像同时安装两个应用变体这样的一些特殊情况需要特殊处理,才能在预期应用中打开给定的网页链接。

多个通过验证的应用

如果您发布了多个应用,其中每个应用都与同一网域相关联,则它们都可以成功地通过验证。不过,如果这些应用可以解析完全相同的网域主机和路径,就像一个应用的精简版和完整版一样,则只有最近安装的应用才能解析该网域的网络 intent。

在这种情况下,请检查用户的设备上是否有可能发生冲突的应用,前提是具有必要的软件包可见性。然后,在您的应用中显示一个自定义选择器对话框,其中包含调用 queryIntentActivities() 所得到的结果。用户可以从该对话框中显示的匹配应用的列表中选择他们偏好的应用。

在您的应用中打开您的应用无法验证的网域

您的应用的主要功能可能是用作第三方应用来打开链接,而无法验证其处理的网域。如果是这种情况,请向用户说明:当他们选择某个网页链接时,无法在第一方应用与您的(第三方)应用之间进行选择。用户需要手动将网域与您的第三方应用相关联。

此外,不妨考虑引入一个对话框或 trampoline activity 来充当代理,让用户能够在第一方应用中打开链接(如果用户愿意这样做的话)。在设置此类对话框或 trampoline activity 之前,请先设置您的应用,使其具有对满足以下条件的第一方应用的软件包可见性:与您的应用的网络 intent 过滤器匹配。