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 的应用。这些应用可能是浏览器。
然后,针对我们要启动的特定网址,查询可处理相关请求的应用。这将返回设置以处理该网址的浏览器和原生应用。
现在,从第二个列表中移除在第一个列表中找到的所有浏览器,这样就只剩下原生应用了。
如果列表为空,就表示没有原生处理程序,系统会返回 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
,我们可以尝试使用新方法发布 nativa 应用。否则,我们会尝试以旧方法发布。
如果启动原生应用失败,我们会启动自定义标签页。
此最佳实践涉及一些样板文件。我们正努力通过将复杂情况封装到库中来简化这一过程。敬请关注 android-browser-helper 支持库的最新动态。
检测支持自定义标签页的浏览器
另一种常见模式是使用 PackageManager 检测设备上的哪些浏览器支持自定义标签页。常见的用例是在 intent 上设置软件包,以避免显示应用消除歧义对话框,或者在连接到自定义标签页服务时选择要连接到的浏览器。
以 API 级别 30 为目标平台时,开发者需要在其 Android 清单中添加一个查询部分,以声明一个 intent 过滤器,以便与支持自定义标签页的浏览器相匹配。
<queries>
<intent>
<action android:name=
"android.support.customtabs.action.CustomTabsService" />
</intent>
</queries>
添加标记后,用于查询支持自定义标签页的浏览器的现有代码就会按预期运行。
常见问题解答
问:查找自定义标签页提供程序的代码会查询可以处理 https://
intent 的应用,但查询过滤器仅声明 android.support.customtabs.action.CustomTabsService
查询。是否应该声明对 https://
intent 的查询?
答:声明查询过滤条件时,它会过滤 PackageManager 查询的响应,而不是查询本身。由于支持自定义标签页的浏览器声明会处理 CustomTabsService,因此它们不会被过滤掉。不支持自定义标签页的浏览器将会被滤除。
总结
这些是调整现有自定义标签页集成以与 Android 11 配合使用所需的全部更改。如需详细了解如何将自定义标签页集成到 Android 应用中,请先查看实现指南,然后查看最佳实践,了解如何构建一流的集成。
如果您有任何疑问或反馈,请告诉我们!