如果你查看
CustomTabsIntent
的源代码,它本质上只是一个创建使用
Intent.ACTION_VIEW
打开URL的常规隐式意图的助手。它帮助您向意图添加额外的数据,这些数据具有Chrome识别的特定键,稍后由Chrome用于呈现自定义UI。
以下是来自
官方页面的解释:
Custom Tabs使用具有关键附加项的ACTION_VIEW意图来自定义UI。这意味着默认情况下,页面将在系统浏览器或用户的默认浏览器中打开。
如果用户已安装Chrome并且它是默认浏览器,则会自动拾取EXTRAS并呈现自定义UI。还可以让另一个浏览器使用Intent extras来提供类似的自定义界面。
对于你链接中的解决方案,源代码来自
这里。正如你从
CustomTabActivityHelper#openCustomTab
中看到的,首先它会查找支持Custom Tabs的应用程序。如果可用,则启动由
CustomTabsIntent
描述的隐式意图。如果没有,则打开
WebViewActivity
。
如何找出是否有应用程序支持Custom Tabs?您可以在
CustomTabsHelper.getPackageNameToUse
中查看它。首先,它将解析所有可以使用
Intent.ACTION_VIEW
打开URL的应用程序。然后,它将检查这些应用程序是否支持Custom Tabs。
然后,
- 如果没有可用的应用,则返回
null
- 如果只有一个可用的应用程序,则返回该应用程序。
- 如果有多个可用的应用程序,其中一个是默认应用程序,则返回该应用程序。
- 如果有多个可用的应用程序,并且其中一个是Chrome,则返回该应用程序。
- 否则,返回
null
- (如果有多个应用程序可用,则可以放置逻辑以要求用户选择他们想要的任何浏览器)
那么,你的解决方案怎么样呢?
如果没有应用程序能处理由
CustomTabsIntent
创建的隐式意图,则会打开
WebviewActivity
,在这种情况下没有安装浏览器?如果我们有浏览器,但没有一个支持Custom Tabs,会发生什么?你的应用程序仍然会要求在浏览器而不是
WebViewActivity
中打开链接。
请记住,CustomTabsIntent
只是一个帮助程序,用于创建一个普通的隐式意图,使用Intent.ACTION_VIEW
打开URL,并使用各种额外数据来自定义UI。如何自定义UI由浏览器处理。基本上,我认为我们可以自己创建并启动意图,以打开具有自定义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();
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;
}
}
STABLE_PACKAGE
很明显,是我们通常使用的Chrome标准版本。至于LOCAL_PACKAGE
,从包名来看,我认为它是系统附带的应用程序。 - marcelljee