Chrome Custom Tabs最佳实践
距离Google发布Chrome Custom Tabs已经一年,Twitter、Medium等国外App早已支持了这个功能,但遗憾的是国内App鲜有支持。这篇文章以官方开发文档和示例源码为基础,加上自己的理解,希望能帮助读者快速掌握Chrome Custom Tabs的用法。
为什么要用Chrome Custom Tabs?
当App需要打开一个网站时,开发者面临两种选择:默认浏览器或WebView。这两种选择都有不足。从App跳转到浏览器是一个非常重的切换,并且浏览器无法自定义;而WebView无法与浏览器共享cookies等数据,并且需要开发者处理非常多的场景。
Chrome Custom Tabs提供了一种新的选择,既能在App和网页之间流畅切换,又能有多种自定义选项。其实它本质上是调用了Chrome中的一个Activity来打开网页,这样想就能理解这些优点了。能自定义的项目有:
Toolbar颜色
进场和出场动画
Toolbar上的action button,menu item和bottom toolbar(通过RemoveView实现)
Chrome Custom Tabs还提供预启动Chrome和预加载网页内容的功能,与传统方式相比加载速度有显著提升。
什么时候用Chrome Custom Tabs,什么时候用WebView?
如果Web页面是你自己的内容(比如淘宝商品页之于手机淘宝),那么WebView是最好的选择,因为你可能需要针对网页内容及用户操作做非常多的自定义。如果是跳到一个外部网站,比如在App中点了一个广告链接跳转到广告商的网站,那么建议使用Chrome Custom Tabs。
前置条件
用户的手机上需要安装Chrome 45或以上版本,并且设为默认浏览器。考虑到Chrome在国内手机上的占有率,这确实是个问题……但如果你的APP不只是面对国内市场,那么以Google在海外市场的影响力,这完全不是问题。
肯定有人要问,如果手机上没有装Chrome,调用Chrome Custom Tabs会发生什么行为呢?我们查看CustomTabsIntent.Builder
的源码可以发现,Builder的内部构造了一个action为Intent.ACTION_VIEW
的Intent,所以答案是调用默认浏览器来打开URL。
这时我们可以发现Chrome Custom Tabs的原理:如果Chrome是默认浏览器,那么这个Intent自然就会唤起Chrome,然后Chrome会根据Intent的各个Extra来配置前面所讲的自定义项。这里有一个隐藏的好处:如果你的工作恰好是开发浏览器,那么也可以根据这些Extra信息来定制界面!
开发向导
快速上手
首先在你的build.gradle文件中加入dependency
1 | dependencies { |
然后写几行代码
1 | String url = "https://www.google.com"; |
就好了!不费吹灰之力~
注意launchUrl这个方法的第一个参数是个Activity。肯定有人会跳起来说,我要在ViewHolder里面处理点击跳转,根本拿不到Activity,只有Context肿么办?!!(其实这个人就是我)
那么我们看看launchUrl的源码:
1 | public void launchUrl(Activity context, Uri url) { |
原来是为了传入startAnimationBundle这个参数来实现自定义转场动画。那么我们自己处理一下就好了:
1 | if (context instanceof Activity) { |
修改Toolbar颜色
你一定希望Chrome Custom Tabs的Toolbar颜色与你自己APP的Toolbar颜色保持相同,看起来就像是在APP内打开网页一样。一行代码搞定:
1 | builder.setToolbarColor(colorInt); |
添加action button
你可能想要在Toolbar上加上action button,那么需要创建一个PendingIntent。下面的代码增加了一个发送邮件的action button:
1 | Intent actionIntent = new Intent(Intent.ACTION_SEND); |
添加menu item
Chrome Custom Tabs的menu默认包含了三个显示为图标的item(Forward, Page Info, Refresh)和两个文字item(Find in page, Open in Browser)。我们可以再添加最多5个文字item。同样需要创建PendingIntent:
1 | Intent menuIntent = new Intent(); |
设置转场动画
如果你的APP设置了转场动画,那么为了统一的用户体验,可以在Chrome Custom Tabs中设置同样的动画
1 | builder.setStartAnimations(this, R.anim.slide_in_right, R.anim.slide_out_left); |
预启动(Warm up) Chrome和预加载
默认情况下,调用了CustomTabsIntent#launchUrl方法之后,才会在后台启动(原文是spin up)Chrome,然后加载网页。这个过程会花费宝贵的时间,并影响到用户体验。要是能「秒开」网页那就爽了。Chrome Custom Tabs可以绑定Chrome的一个Service,绑定成功之后可以预启动Chrome,还可以让Chrome预加载一些网页(当然这是要消耗一些流量的)。大致的过程是这样的:
- 通过CustomTabsClient#bindCustomTabsService方法绑定Service。绑定成功后会得到一个CustomTabsClient的instance。如果绑定失败,说明Chrome版本过低或者没有安装。
- 通过CustomTabsClient#warmup方法在后台启动Chrome。
- 通过CustomTabsClient#newSession方法创建一个新的session。这一步还可以传入一个CustomTabsCallback参数用来得到session的状态,比如页面是否加载完成。
- 通过CustomTabsSession#mayLaunchUrl来告诉Chrome要预加载哪些网页。
- 当用户点击事件发生时,将前面那个session传入CustomTabsIntent.Builder,然后用文章最开头的方法打开网页。
下面是完成这个过程的简单代码:
1 | public class MainActivity extends Activity implements ServiceConnectionCallback { |
简单的预启动
Android Support Library 24.0.0开始加入了CustomTabsClient#connectAndInitialize方法来简化预启动代码。如果你无法预料到用户会打开哪个URL,或者出于省电的考虑不想预加载URL,那么可以在Activity的onStart中加入一行代码来预启动。
1 | CustomTabsClient.connectAndInitialize(this, "com.android.chrome"); |
最佳实践
绑定Custom Tabs的service并预启动
预启动Chrome可以帮助你节省高达700ms的宝贵时间!这几乎是可以划分卡与不卡的差别。启动过程是在后台以低优先级进行,所以不会对你的APP性能有负面影响。
预加载网页内容
预加载网页后可以达到秒开的效果!所以如果你至少有50%的把握用户会打开某个URL,你应当调用mayLaunchUrl()方法。这个方法会提前下载并渲染网页内容,但不可避免的会有一点流量和电量的消耗。如果用户正在使用收费的数据流量,或者手机电量不足,那么这个方法不会生效。所以我们完全不用自己考虑性能优化。
备选方案
如果用户的手机上没有安装Chrome,那么打开默认浏览器可能并不是最好的用户体验。所以如果在bindService那一步失败了,无论是打开默认浏览器还是WebView,选择一个你认为最好的备选方案。
referrer
很多网站都会统计自己的流量是从哪儿来的,所以最好告诉他们是你的帅气APP给他们带来了流量:
1 | intent.putExtra(Intent.EXTRA_REFERRER, |
加入自定义动画
自定义的转场动画会让你的网页跳转更流畅。确保进场动画和出场动画是反向的,比如网页从右边进来,就从右边出去,这样能帮助用户理解跳转关系。
1 | builder.setStartAnimations(this, R.anim.slide_in_right, R.anim.slide_out_left); |
为Action Button选个合适的图标
一个合适的图标能让用户快速的理解到APP的功能。但记住图标的最大尺寸是宽48dp高24dp。
其他支持的浏览器
前面有讲到其他浏览器也有机会支持Custom Tabs的功能(虽然我还没发现有哪款已经支持了)。如果你检测到不止一个浏览器支持Custom Tabs,那么第一次调用时最好询问用户打算用哪个浏览器。
1 | /** |
给用户选择权
如果你的APP之前一直是用默认浏览器打开URL,后来才加入的Custom Tabs,那么老用户可能希望保留原来的习惯。可以考虑在设置里面增加一个选项,让用户自行选择。
尽量让Native APP处理URL
有些URL可以由Native APP处理。如果用户安装了Twitter的APP并且点击了Twitter的URL,他可能更期望用Twitter APP打开。所以在打开URL之前,检查看看手机里有没有其他APP可以处理这个URL。
自定义Toolbar颜色
如果你希望用户觉得网页内容是你的APP的一部分,那么就将Toolbar颜色设为你的primaryColor。如果希望清除的表明网页内容与APP无关,那么选个不同的颜色吧。
增加一个分享按钮
用户可能想要把URL分享给好友,但Custom Tabs默认并没有分享按钮,所以最好自己加个吧。
自定义关闭按钮
Custom Tabs左上角的关闭按钮默认是一个叉叉。如果你希望用户感觉到网页内容是APP的一部分,那么最好把叉叉换成返回按钮。
1 | builder.setCloseButtonIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_arrow_back)); |
分清内部链接和外部链接
举个例子,如果用户在Twitter APP里点击了一个http://twitter.com
开头的URL,那么应该在APP内部处理。Custom Tabs只应用来处理外部链接。
处理连击
如果你想在用户点击URL到打开Custom Tabs之间的这段时间做一点准备工作,确保不要超过100ms,否则用户可能会觉得APP没有反应而再次点击。
然而你懂的在Android上是无法完全避免卡顿的,所以当用户反复点击同一个URL时,你应该只将URL打开一次。
结尾
如果你的APP是使用默认浏览器打开URL,那么身为一个合格的开发者,即使你的APP只在国内上架,也应该加入Custom Tabs支持。毕竟国内还是有一些会科学上网的用户使用Chrome的。更重要的是,我们这些开发者如果看到有国内APP使用了Custom Tabs,会欣慰的点个赞!