的以下版本如果您通过CustomTabsIntent
源代码中看到的,它无非只是一个辅助创建一个普通的隐含意图使用Intent.ACTION_VIEW
打开URL等等。它可以帮助您使用Chrome识别的特定键为意图添加额外数据,Chrome随后将使用该键来呈现自定义用户界面。
这里是the official page的解释:
自定义选项卡使用与关键额外的ACTION_VIEW意向来定制 的UI。这意味着默认情况下,页面将在系统 浏览器或用户的默认浏览器中打开。
如果用户安装了Chrome并且它是默认浏览器,它将自动选取EXTRAS并呈现自定义UI。它 也可以让另一个浏览器使用Intent extras来为 提供一个类似的定制界面。
对于链接上的解决方案,源代码取自here。正如您从CustomTabActivityHelper#openCustomTab
可以看到的,首先它会查找支持自定义选项卡的应用程序。如果可用,则启动CustomTabsIntent
所述的隐含意图。如果没有,请打开WebViewActivity
。
如何找出是否有任何应用程序支持自定义选项卡?你可以选择CustomTabsHelper.getPackageNameToUse
。首先,它会解析所有可以使用Intent.ACTION_VIEW
打开URL的应用程序。然后,它会检查该应用是否支持自定义标签。
然后,
- 如果没有应用程序可用,则返回
null
- 如果只有1个可用的应用程序,将其返回。
- 如果超过1个应用程序可用,其中1个是默认应用程序,请将其返回。
- 如果有超过1个应用可用,其中1个是Chrome,请将其退回。
- 否则,返回
null
- (如果超过1级可用的应用程序,你可以把一个逻辑来要求用户选择他们想要的任何浏览器)
现在,如何您的解决方案?
如果我们使用您的解决方案,WebviewActivity
将被打开,如果没有应用程序可以处理CustomTabsIntent
创建的隐式意图,在这种情况下,没有安装浏览器?如果我们有浏览器,而且它们都不支持自定义选项卡,会发生什么情况?您的应用仍然会要求在浏览器中打开链接,而不是在WebViewActivity
中。
请记住,CustomTabsIntent
只是一个帮手,用于创建一个普通的隐式意图打开一个URL使用Intent.ACTION_VIEW
与各种EXTRA数据来定制的用户界面。如何自定义UI由浏览器处理。基本上,我认为我们可以创建并开始使用自定义用户界面来打开浏览器,但不需要CustomTabsIntent
。我从来没有尝试过这个。
如果你想链接到任何浏览器打开,不管浏览器支持自定义选项卡或没有,该链接将在WebViewActivity
打开,如果没有可用的应用程序,您的解决方案解决它,即使它是不是我想的最好的解决方案。
但是,如果你希望链接在浏览器,支持自定义选项卡被打开,并且链接到WebViewActivity
如果没有应用程序,支持自定义选项卡提供被打开,他的解决办法是正确的。
但是,如果你想要的只是提供回退机制,它不应该那么复杂。这里是更简单的代码:
public class CustomTabs {
private static final String ACTION_CUSTOM_TABS_CONNECTION = "android.support.customtabs.action.CustomTabsService";
private static final String STABLE_PACKAGE = "com.android.chrome";
private static final String BETA_PACKAGE = "com.chrome.beta";
private static final String DEV_PACKAGE = "com.chrome.dev";
private static final String LOCAL_PACKAGE = "com.google.android.apps.chrome";
public static void openTab(Context context, String url) {
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
/* do some UI customization here */
CustomTabsIntent customTabsIntent = builder.build();
String packageName = getPackageNameToUse(context);
if (packageName == null) {
Intent intent = new Intent(context, WebviewActivity.class);
intent.putExtra(WebviewActivity.EXTRA_URL, url);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(intent);
} else {
customTabsIntent.intent.setPackage(packageName);
customTabsIntent.launchUrl(context, Uri.parse(url));
}
}
private static String getPackageNameToUse(Context context) {
String packageNameToUse = null;
PackageManager pm = context.getPackageManager();
Intent activityIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.example.com"));
ResolveInfo defaultViewHandlerInfo = pm.resolveActivity(activityIntent, 0);
String defaultViewHandlerPackageName = null;
if (defaultViewHandlerInfo != null) {
defaultViewHandlerPackageName = defaultViewHandlerInfo.activityInfo.packageName;
}
List<ResolveInfo> resolvedActivityList = pm.queryIntentActivities(activityIntent, 0);
List<String> packagesSupportingCustomTabs = new ArrayList<>();
for (ResolveInfo info : resolvedActivityList) {
Intent serviceIntent = new Intent();
serviceIntent.setAction(ACTION_CUSTOM_TABS_CONNECTION);
serviceIntent.setPackage(info.activityInfo.packageName);
if (pm.resolveService(serviceIntent, 0) != null) {
packagesSupportingCustomTabs.add(info.activityInfo.packageName);
}
}
if (packagesSupportingCustomTabs.isEmpty()) {
packageNameToUse = null;
} else if (packagesSupportingCustomTabs.size() == 1) {
packageNameToUse = packagesSupportingCustomTabs.get(0);
} else if (!TextUtils.isEmpty(defaultViewHandlerPackageName)
&& !hasSpecializedHandlerIntents(context, activityIntent)
&& packagesSupportingCustomTabs.contains(defaultViewHandlerPackageName)) {
packageNameToUse = defaultViewHandlerPackageName;
} else if (packagesSupportingCustomTabs.contains(STABLE_PACKAGE)) {
packageNameToUse = STABLE_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(BETA_PACKAGE)) {
packageNameToUse = BETA_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(DEV_PACKAGE)) {
packageNameToUse = DEV_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(LOCAL_PACKAGE)) {
packageNameToUse = LOCAL_PACKAGE;
}
return packageNameToUse;
}
private static boolean hasSpecializedHandlerIntents(Context context, Intent intent) {
try {
PackageManager pm = context.getPackageManager();
List<ResolveInfo> handlers = pm.queryIntentActivities(intent, PackageManager.GET_RESOLVED_FILTER);
if (handlers == null || handlers.size() == 0) {
return false;
}
for (ResolveInfo resolveInfo : handlers) {
IntentFilter filter = resolveInfo.filter;
if (filter == null) continue;
if (filter.countDataAuthorities() == 0 || filter.countDataPaths() == 0) continue;
if (resolveInfo.activityInfo == null) continue;
return true;
}
} catch (RuntimeException e) {
Log.e("LOG", "Runtime exception while getting specialized handlers");
}
return false;
}
}
嗨,感谢这篇文章!但是LOCAL_PACKAGE和STABLE_PACKAGE有什么区别?为什么你允许用户在这里使用LOCAL_PACKAGE? –
@JiechaoWang说实话,我不知道。代码来自Chrome团队提供的示例代码。 'STABLE_PACKAGE'很明显。这是我们通常使用的标准版Chrome。对于'LOCAL_PACKAGE',从包名称来看,我认为这是系统附带的应用程序。 – marcelljee