搜狐媒体平台-搜狐网站>IT

他一出家就成中国最帅和尚

眼眸深邃、轮廓分明、身材颀长,活生生的一幅画。

大学副教授与在押服刑女结婚

这在监狱民警看来,那么令人不可思议。

Chrome?Custom?Tabs最佳实践

声明:本文由入驻搜狐公众平台的作者撰写,除搜狐官方账号外,观点仅代表作者本人,不代表搜狐立场。举报

  声明:本文为 赵元杰 原创,授权发布在 Android程序员公众号,转载请参考原文协议。

  原文:https://qq157755587.github.io/2016/08/12/custom-tabs-best-practices/

  今天的文章来自武汉时光小屋团队的 赵元杰 同学,很多人知道我目前也在武汉工作,武汉的Android圈里比较活跃的同学我认识不太多,元杰是其中较熟的一位,我们都做海外方向业务,有时会简单交流,偶尔会在一些线下技术活动中碰到他,也一起吃饭聊过,印象中的他内敛沉稳,从业十年依然保持对技术最初的热情,在Kotlin正式发布前,就已经在他们公司正式项目中用上了Kotlin,真可谓艺高人胆大,其博客产量不高,但质量很高,推荐大家关注

  https://qq157755587.github.io/

  今天的主题 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

  dependencies {

  ...

  compile 'com.android.support:customtabs:24.1.1'

  }

  然后写几行代码

  String url = "https://www.google.com";

  CustomTabsIntent.Builder builder

  = newCustomTabsIntent.Builder();

  CustomTabsIntent customTabsIntent

  = builder.build();

  customTabsIntent.launchUrl(

  this, Uri.parse(url));

  就好了!不费吹灰之力~

  注意launchUrl这个方法的第一个参数是个Activity。肯定有人会跳起来说,我要在ViewHolder里面处理点击跳转,根本拿不到Activity,只有Context肿么办?!!(其实这个人就是我)

  那么我们看看launchUrl的源码:

  publicvoidlaunchUrl(

  Activity context, Uri url){

  intent.setData(url);

  ActivityCompat.startActivity(

  context,

  intent,

  startAnimationBundle); }

  原来是为了传入startAnimationBundle这个参数来实现自定义转场动画。那么我们自己处理一下就好了:

  if(context instanceofActivity) { customTabsIntent.launchUrl(

  (Activity) context, uri); } else{ Intent intent

  = customTabsIntent.intent; intent.setData(uri);

  if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {

  context.startActivity(

  intent,

  customTabsIntent.startAnimationBundle); } else{ context.startActivity(intent); } } 修改Toolbar颜色

  你一定希望Chrome Custom Tabs的Toolbar颜色与你自己APP的Toolbar颜色保持相同,看起来就像是在APP内打开网页一样。一行代码搞定:

  builder.setToolbarColor(colorInt); 添加action button

  你可能想要在Toolbar上加上action button,那么需要创建一个PendingIntent。下面的代码增加了一个发送邮件的action button:

  Intent actionIntent

  = newIntent(Intent.ACTION_SEND);

  actionIntent.setType( "*/*"); actionIntent.putExtra(

  Intent.EXTRA_EMAIL,

  "example@example.com"); actionIntent.putExtra(

  Intent.EXTRA_SUBJECT,

  "example");

  PendingIntent pi

  = PendingIntent.getActivity(

  this, 0, actionIntent, 0);

  //注意在正式项目中不要在UI线程读取图片

  Bitmap icon

  = BitmapFactory.decodeResource(

  getResources(),

  R.drawable.ic_share);

  builder.setActionButton(

  icon,

  "send email",

  pi,

  true); 添加menu item

  Chrome Custom Tabs的menu默认包含了三个显示为图标的item(Forward, Page Info, Refresh)和两个文字item(Find in page, Open in Browser)。我们可以再添加最多5个文字item。同样需要创建PendingIntent:

  Intent menuIntent = newIntent();

  menuIntent.setClass(

  getApplicationContext(),

  SomeActivity.class);

  PendingIntent pi

  = PendingIntent.getActivity(

  getApplicationContext(),

  0,

  menuIntent,

  0);

  builder.addMenuItem(

  "Menu entry 1",

  pi); 设置转场动画

  如果你的APP设置了转场动画,那么为了统一的用户体验,可以在Chrome Custom Tabs中设置同样的动画

  builder.setStartAnimations(

  this,

  R.anim.slide_in_right,

  R.anim.slide_out_left);

  builder.setExitAnimations(

  this,

  R.anim.slide_in_left,

  R.anim.slide_out_right); 预启动(Warm up) Chrome和预加载

  默认情况下,调用了CustomTabsIntent#launchUrl方法之后,才会在后台启动(原文是spin up)Chrome,然后加载网页。这个过程会花费宝贵的时间,并影响到用户体验。要是能「秒开」网页那就爽了。Chrome Custom Tabs可以绑定Chrome的一个Service,绑定成功之后可以预启动Chrome,还可以让Chrome预加载一些网页(当然这是要消耗一些流量的)。大致的过程是这样的:

  下面是完成这个过程的简单代码:

  publicclassMainActivity

  extendsActivity

  implementsServiceConnectionCallback{

  privateCustomTabsSession mSession;

  privateCustomTabsClient mClient;

  privateCustomTabsServiceConnection mConnection;

  ...

  privatevoidbindCustomTabsService(){

  mConnection = newServiceConnection( this);

  CustomTabsClient.bindCustomTabsService(

  this,

  "com.android.chrome",

  mConnection);

  }

  privatevoidwarmup(){

  if(mClient != null)

  mClient.warmup( 0);

  }

  privatevoidpreLaunch(String url){

  if(mClent != null

  && mSession == null) {

  mSession = mClient.newSession(

  newCustomTabsCallback({

  @Override

  publicvoidonNavigationEvent(

  intnavigationEvent,

  Bundle extras){

  //这里可以取到Session的状态

  }

  }));

  }

  //这里的第三个参数可以传入一些低优先级

  //的Url,但不能保证会被预加载

  mSession.mayLaunchUrl(

  Uri.parse(url), null, null);

  }

  privatevoidlaunch(String url){

  CustomTabsIntent.Builder builder

  = newCustomTabsIntent.Builder(mSession);

  CustomTabsIntent customTabsIntent = builder.build();

  customTabsIntent.launchUrl( this, Uri.parse(url)); }

  @Override

  publicvoidonDestroy(){

  if(mConnection != null) {

  unbindService(mConnection);

  mClient = null;

  mSession = null;

  }

  super.onDestroy();

  }

  // ServiceConnectionCallback的回调方法

  @Override

  publicvoidonServiceConnected(

  CustomTabsClient client){

  mClient = client;

  }

  @Override

  publicvoidonServiceDisconnected(){

  mClient = null;

  }

  } 简单的预启动

  Android Support Library 24.0.0开始加入了CustomTabsClient#connectAndInitialize方法来简化预启动代码。如果你无法预料到用户会打开哪个URL,或者出于省电的考虑不想预加载URL,那么可以在Activity的onStart中加入一行代码来预启动。

  CustomTabsClient

  .connectAndInitialize(

  this, "com.android.chrome"); 最佳实践 绑定Custom Tabs的service并预启动

  预启动Chrome可以帮助你节省高达700ms的宝贵时间!这几乎是可以划分卡与不卡的差别。启动过程是在后台以低优先级进行,所以不会对你的APP性能有负面影响

  预加载网页内容

  预加载网页后可以达到秒开的效果!所以如果你至少有50%的把握用户会打开某个URL,你应当调用mayLaunchUrl()方法。这个方法会提前下载并渲染网页内容,但不可避免的会有一点流量和电量的消耗。如果用户正在使用收费的数据流量,或者手机电量不足,那么这个方法不会生效。所以我们完全不用自己考虑性能优化

  备选方案

  如果用户的手机上没有安装Chrome,那么打开默认浏览器可能并不是最好的用户体验。所以如果在bindService那一步失败了,无论是打开默认浏览器还是WebView,选择一个你认为最好的备选方案。

  referrer

  很多网站都会统计自己的流量是从哪儿来的,所以最好告诉他们是你的帅气APP给他们带来了流量:

  intent.putExtra(

  Intent.EXTRA_REFERRER,

  Uri.parse(

  Intent.URI_ANDROID_APP_SCHEME

  + "//"

  + context.getPackageName())); 加入自定义动画

  自定义的转场动画会让你的网页跳转更流畅。确保进场动画和出场动画是反向的,比如网页从右边进来,就从右边出去,这样能帮助用户理解跳转关系。

  builder.setStartAnimations(

  this,

  R.anim.slide_in_right,

  R.anim.slide_out_left);

  builder.setExitAnimations(

  this,

  R.anim.slide_in_left,

  R.anim.slide_out_right); 为Action Button选个合适的图标

  一个合适的图标能让用户快速的理解到APP的功能。但记住图标的最大尺寸是宽48dp高24dp。

  其他支持的浏览器

  前面有讲到其他浏览器也有机会支持Custom Tabs的功能(虽然我还没发现有哪款已经支持了)。如果你检测到不止一个浏览器支持Custom Tabs,那么第一次调用时最好询问用户打算用哪个浏览器。

  /** * Returns a list of packages that support Custom Tabs. */

  publicstaticArrayList getCustomTabsPackages(Context context){

  PackageManager pm = context.getPackageManager();

  // Get default VIEW intent handler.

  Intent activityIntent

  = newIntent(

  Intent.ACTION_VIEW,

  Uri.parse( "https://www.example.com"));

  // Get all apps that can handle VIEW intents.

  List resolvedActivityList

  = pm.queryIntentActivities(activityIntent, 0);

  ArrayList packagesSupportingCustomTabs = newArrayList<>();

  for(ResolveInfo info : resolvedActivityList) {

  Intent serviceIntent = newIntent();

  serviceIntent.setAction(

  ACTION_CUSTOM_TABS_CONNECTION);

  serviceIntent.setPackage(

  info.activityInfo.packageName);

  // Check if this package also

  // resolves the Custom Tabs service.

  if(pm.resolveService(serviceIntent, 0) != null) {

  packagesSupportingCustomTabs.add(info);

  }

  }

  returnpackagesSupportingCustomTabs;

  } 给用户选择权

  如果你的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的一部分,那么最好把叉叉换成返回按钮。

  builder.setCloseButtonIcon(

  BitmapFactory.decodeResource(

  getResources(),

  R.drawable.ic_arrow_back)); 分清内部链接和外部链接

  举个例子,如果用户在Twitter APP里点击了一个https://twitter.com开头的URL,那么应该在APP内部处理。Custom Tabs只应用来处理外部链接。

  处理连击

  如果你想在用户点击URL到打开Custom Tabs之间的这段时间做一点准备工作,确保不要超过100ms,否则用户可能会觉得APP没有反应而再次点击。

  然而你懂的在Android上是无法完全避免卡顿的,所以当用户反复点击同一个URL时,你应该只将URL打开一次。

  结尾

  如果你的APP是使用默认浏览器打开URL,那么身为一个合格的开发者,即使你的APP只在国内上架,也应该加入Custom Tabs支持。毕竟国内还是有一些会科学上网的用户使用Chrome的。更重要的是,我们这些开发者如果看到有国内APP使用了Custom Tabs,会欣慰的点个赞!

  赞赏支持 元杰

  赞赏

  人赞赏

mt.sohu.com true Android程序员mp https://mt.sohu.com/20160813/n464069637.shtml report 14151 声明:本文为赵元杰原创,授权发布在Android程序员公众号,转载请参考原文协议。原文:https://qq157755587.github.io/2016/08
阅读(0) 举报
欢迎举报抄袭、转载、暴力色情及含有欺诈和虚假信息的不良文章。

热门关注

搜生活

搜生活+关注

搜狐公众平台官方账号

MAGIC杨梦晶

MAGIC杨梦晶+关注

生活时尚&搭配博主 /生活时尚自媒体 /时尚类书籍作者

搜狐教育

搜狐教育+关注

搜狐网教育频道官方账号

星吧GEO

星吧GEO+关注

全球最大华文占星网站-专业研究星座命理及测算服务机构

热门图片

  • 热点视频
  • 影视剧
  • 综艺
  • 原创
锦绣缘

同步热播-锦绣缘

主演:黄晓明/陈乔恩/乔任梁/谢君豪/吕佳容/戚迹
神雕侠侣

大结局-神雕侠侣

主演:陈晓/陈妍希/张馨予/杨明娜/毛晓彤/孙耀琦
封神英雄榜

同步热播-封神英雄榜

主演:陈键锋/李依晓/张迪/郑亦桐/张明明/何彦霓

六颗子弹

主演:尚格·云顿/乔·弗拉尼甘/Bianca Bree
龙虎少年队2

龙虎少年队2

主演:艾斯·库珀/ 查宁·塔图姆/ 乔纳·希尔

《奔跑吧兄弟》

baby14岁写真曝光

《我看你有戏》

李冰冰向成龙撒娇争宠

《明星同乐会》

李湘遭闺蜜曝光旧爱

《非你莫属》

美女模特教老板走秀

《一站到底》

曝搬砖男神奇葩择偶观

搜狐视频娱乐播报

柳岩被迫成赚钱工具

大鹏嘚吧嘚

大屁小P虐心恋

匆匆那年第16集

匆匆那年大结局

隐秘而伟大第二季

乔杉遭粉丝骚扰

The Kelly Show

男闺蜜的尴尬初夜

我来说两句排行榜

客服热线:86-10-58511234

客服邮箱:kf@vip.sohu.com