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 应用中,请先参阅实现指南,然后查看最佳实践,了解如何构建一流的集成。
如果您有任何疑问或反馈,欢迎与我们联系!