在 Android 11 中使用自定义标签页

Android 11 对应用与用户在设备上安装的其他应用交互的方式做出了更改。如需详细了解这些变更,请参阅 Android 文档

如果使用自定义标签页的 Android 应用以 SDK 级别 30 或更高版本为目标平台,则可能需要进行一些更改。本文介绍了这些应用可能需要进行的更改。

在最简单的情况下,您可以使用一行代码启动自定义标签页,如下所示:

new CustomTabsIntent.Builder().build()
        .launchUrl(this, Uri.parse("https://www.example.com"));

使用此方法启动应用或添加界面自定义(例如更改工具栏颜色添加操作按钮)的应用无需在应用中进行任何更改。

优先使用原生应用

不过,如果您遵循了最佳实践,可能需要进行一些更改。

第一个相关最佳实践是,如果已安装能够处理 intent 的应用,应用应优先使用原生应用来处理 intent,而不是自定义标签页。

在搭载 Android 11 及更高版本的设备上

Android 11 引入了一个新的 intent 标记 FLAG_ACTIVITY_REQUIRE_NON_BROWSER,这是尝试打开原生应用的推荐方式,因为它不需要应用声明任何软件包管理器查询。

static boolean launchNativeApi30(Context context, Uri uri) {
    Intent nativeAppIntent = new Intent(Intent.ACTION_VIEW, uri)
            .addCategory(Intent.CATEGORY_BROWSABLE)
            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                    Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER);
    try {
        context.startActivity(nativeAppIntent);
        return true;
    } catch (ActivityNotFoundException ex) {
        return false;
    }
}

解决方法是尝试启动 intent,并使用 FLAG_ACTIVITY_REQUIRE_NON_BROWSER 要求 Android 在启动时避免使用浏览器。

如果未找到能够处理此 intent 的原生应用,系统会抛出 ActivityNotFoundException

Android 11 之前

即使应用以 Android 11 或 API 级别 30 为目标平台,但较低版本的 Android 也不支持 FLAG_ACTIVITY_REQUIRE_NON_BROWSER 标志,因此在这些情况下,我们需要查询软件包管理器:

private static boolean launchNativeBeforeApi30(Context context, Uri uri) {
    PackageManager pm = context.getPackageManager();

    // Get all Apps that resolve a generic url
    Intent browserActivityIntent = new Intent()
            .setAction(Intent.ACTION_VIEW)
            .addCategory(Intent.CATEGORY_BROWSABLE)
            .setData(Uri.fromParts("http", "", null));
    Set<String> genericResolvedList = extractPackageNames(
            pm.queryIntentActivities(browserActivityIntent, 0));

    // Get all apps that resolve the specific Url
    Intent specializedActivityIntent = new Intent(Intent.ACTION_VIEW, uri)
            .addCategory(Intent.CATEGORY_BROWSABLE);
    Set<String> resolvedSpecializedList = extractPackageNames(
            pm.queryIntentActivities(specializedActivityIntent, 0));

    // Keep only the Urls that resolve the specific, but not the generic
    // urls.
    resolvedSpecializedList.removeAll(genericResolvedList);

    // If the list is empty, no native app handlers were found.
    if (resolvedSpecializedList.isEmpty()) {
        return false;
    }

    // We found native handlers. Launch the Intent.
    specializedActivityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(specializedActivityIntent);
    return true;
}

此处所用的方法是查询软件包管理器,以查找支持通用 http intent 的应用。这些应用可能是浏览器。

然后,查询为我们要启动的特定网址处理 intent 的应用。这将返回用于处理该网址的浏览器和原生应用设置。

现在,从第二个列表中移除第一个列表中找到的所有浏览器,我们将只剩下原生应用。

如果列表为空,则表示没有原生处理脚本,并返回 false。否则,我们会为原生处理程序启动 intent。

综合应用

我们需要确保针对每种情况使用正确的方法:

static void launchUri(Context context, Uri uri) {
    boolean launched = Build.VERSION.SDK_INT >= 30 ?
            launchNativeApi30(context, uri) :
            launchNativeBeforeApi30(context, uri);

    if (!launched) {
        new CustomTabsIntent.Builder()
                .build()
                .launchUrl(context, uri);
    }
}

Build.VERSION.SDK_INT 提供我们需要的信息。如果该值等于或大于 30,Android 会知道 FLAG_ACTIVITY_REQUIRE_NON_BROWSER,我们可以尝试使用新方法启动原生应用。否则,我们会尝试使用旧方法启动。

如果启动原生应用失败,我们会启动自定义标签页。

此最佳实践涉及一些样板代码。我们正在努力通过将复杂性封装在库中来简化此过程。敬请关注 android-browser-helper 支持库的更新。

检测支持 Custom Tabs 的浏览器

另一种常见模式是使用 PackageManager 在设备上检测哪些浏览器支持 Custom Tabs。常见用例包括在 intent 上设置软件包以避免显示应用歧义消除对话框,或在连接到自定义标签页服务时选择要连接到的浏览器。

以 API 级别 30 为目标平台时,开发者需要向其 Android 清单中添加一个查询部分,声明一个与支持 Custom Tabs 的浏览器匹配的 intent 过滤器。

<queries>
    <intent>
        <action android:name=
            "android.support.customtabs.action.CustomTabsService" />
    </intent>
</queries>

添加完标记后,用于查询支持自定义标签页的浏览器的现有代码将按预期运行。

常见问题解答

问题:用于查找自定义标签页提供程序的代码会查询可处理 https:// intent 的应用,但查询过滤器仅声明 android.support.customtabs.action.CustomTabsService 查询。不应声明对 https:// intent 的查询吗?

答:声明查询过滤条件时,它会过滤对 PackageManager 的查询响应,而不是过滤查询本身。由于支持自定义标签页的浏览器会声明处理 CustomTabsService,因此不会被滤除。系统会滤除不支持自定义标签页的浏览器。

总结

以上就是将现有 Custom Tabs 集成调整为适用于 Android 11 所需的所有更改。如需详细了解如何将自定义标签页集成到 Android 应用中,请先参阅实现指南,然后查看最佳实践,了解如何构建一流的集成。

如果您有任何疑问或反馈,欢迎与我们联系!