• 呼起客户端


    #黑科技# 跳出浏览器

    当移动浪潮来袭,不论是传统 PC 网站/应用,还是新兴的移动互联网,都一并蜂拥的走进用户的手机。提供一个便于手机浏览的 Web 页面,再造一个功能丰富的移动 App 成了每个产品的标配。提供移动 Web 页面,可以使得用户更易获取产品信息,在微信上、微博中,搜索里点个链接,就可以立刻享用产品功能;而提供移动 App,则可以提供更好的产品体验,使用 native api 能构建更丰富的特色功能,提供更出众的性能表现。


    来想象一下这个场景,当别人发给你一个链接,是知乎问题「豌豆荚的员工工作方式是什么样的?」,你在手机浏览器上慢吞吞的加载好了,答案看得特别激动想点个赞。结果发现,还!要!登!录!我明明已经安装了知乎的 App 好吗!为啥不让我愉快的在知乎 App 上操作呐?

     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     

    这就是本 #黑科技# 的主题,有了移动 Web 页面,又提供了移动 App,如何能让两者更完美的结合在一起呢?当用户已经安装了 App 的前提下,访问移动端 Web 页面时,可以无缝的跳转到 App 中对应的位置去?

    依然举上面那个栗子,知乎的同学其实已经有了解决方案,就是「打开应用」那个按钮,如果已经安装了知乎 App,点击后就会从浏览器跳转到应用中了。类似于这样的按钮,背后的技术方案是什么?有哪些局限性?有没有什么一招搞定的必杀技?这就是本文讨论的主要内容。

    第一招:拦截 Http 跳转

    在 Android 中,最标准的方式,就是在应用的配置文件 AndroidManifest.xml 中,通过 <activity> 标签里的 <intent-filter> 来声明:“本应用可以更好的处理某些 url 对应的页面,浏览器你交给我吧”。套在例子上,声明形如:

    <activity android:name=”com.zhihu.android.QuestionActivity”>
      <intent-filter>
        <action android:name=”android.intent.action.VIEW” />
        <category android:name=”android.intent.category.DEFAULT” />
        <category android:name=”android.intent.category.BROWSABLE” />
        <!-- 关键所在,匹配相应域名和 url 模式 -->
        <data android:scheme=”http” android:host=”www.zhihu.com” 
    android:pathPattern=”/question/.*” />
      </intent-filter>
    </activity>
    

    做了上述的声明之后,在 Chrome 里访问 豌豆荚的员工工作方式是什么样的?,便可以跳转到知乎 Android 客户端,并打开这个问题的页面。不过这个解决方案有挺多问题,最重要的一个原因是:“兼容性”。

    除了 Chrome,从豌豆荚上的下载量看,最热门的手机浏览器是这些:

    而以上浏览器,大都不遵守 Android 的协定,不支持通过匹配 url 跳转到更适合的应用中去。臆测其原因,大抵是国内浏览器都不愿将流量导给其他应用吧。

    第二招:自定义 Scheme

    如此,那就另辟蹊径,既然 http 协议的 url 会被很多浏览器拦自行处理掉,那就不用 http 协议而采用自定义的 scheme 试试看。


    将 AndroidManifest.xml 中的声明修改如下:

    <activity android:name=”com.zhihu.android.QuestionActivity”>
      <intent-filter>
        <action android:name=”android.intent.action.VIEW” />
        <category android:name=”android.intent.category.DEFAULT” />
        <category android:name=”android.intent.category.BROWSABLE” />
        <!-- 关键所在,匹配相应的 scheme -->
        <data android:scheme=”zhihu” android:host=”questions” />
      </intent-filter>
    </activity>
    

    把「打开应用」的跳转链接设置为形如 ”zhihu://questions/…“ 的 url,点击后就可以匹配跳转到应用对应的 activity 中去。当然,如果简单的使用 <a> 标签来做这件事情,若手机中未安装知乎客户端,点击后就会跳转到一个错误页面(地址是 zhihu://questions/…)。解决方案也简单,使用 <iframe> 即可,详情就不在此赘述。

    第三招:Chrome Intent

    自定义的 scheme 可以搞定很多浏览器,但 Chrome 除外。原因是为了更有序的打通浏览器页面和本地应用,Chrome 25 后不再支持自定义的 scheme,而推出了 Chrome Intent,作为标准协议进行推广,其格式形如:

    intent:
      //scan/
      #Intent; 
        package=com.google.zxing.client.android; 
        scheme=zxing; 
        end;
    

    Chrome Intent 首先将 scheme 统一为 ”intent“,大量信息放到了锚点 ”#“ 之后,称作 ”fragment“(此 fragment 非彼 fragment),它描述了由谁来接收这个 uri。fragment 中可以指定打开这个 uri 的包名,或者是 action、extra,等等。使用 Intent.parseUri 函数可以将这样的 uri 直接转成一个 intent 对象,反之调用 Intent.toUri 函数可将 intent 对象序列化如此格式的 uri。


    应用到知乎这个例子里,在 AndroidManifest.xml 中的声明与自定义 scheme 写法完全一致,只是在调用时,需要在跳转链接中写成如下格式:

    intent:
      //questions/...
      #Intent; 
        package=com.zhihu.android;
        scheme=zhihu; 
        end; 

    呼微博客户端:
    <a id="test" href="intent:#Intent;package=com.sina.weibo;scheme=sinaweibo://splash/;end">test</a>

     

     

    第四招:open方式

    呼微博客户端:

    var o = "sinaweibo://splash/";
    var j = window.open(o, "_blank");
    setTimeout(function() {
          j.close();
    }, 0);



     

    ome Intent 首先将 scheme 统一为 ”intent“,大量信息放到了锚点 ”#“ 之后,称作 ”fragment“(此 fragment 非彼 fragment),它描述了由谁来接收这个 uri。fragment 中可以指定打开这个 uri 的包名,或者是 action、extra,等等。使用 Intent.parseUri 函数可以将这样的 uri 直接转成一个 int

     

    必杀技:内嵌 Http 服务

    至此,只要利用 UA 信息,合理使用自定义 scheme 和 Chrome Intent,就可以搞定市面上几乎全部的浏览器,这就完了吗?当然没有!


    随着以微信为代表的社交应用的不断发展,它内嵌的 WebView 已然成为一个轻型浏览器了,坐拥巨大的用户和内容分享量,微信等应用带来的页面访问量是不容忽视的。但这些应用的 WebView 通常是禁止外链的,不论是什么 scheme 在这里一律不好使,这就使得分享到微信的知乎问题,就是再点击「打开应用」都是无效的。


    有办法解决么?当然,是有的,”黑科技“ 粉墨登场的时刻到了。大家都知道,web 页面可以发起 ajax 请求用来与服务器交互,如果这个 ”服务器“ 不在云端,而是在本机呢?没错,解决方案就是在应用中绑定本地端口,启动一个 http 服务,来响应发送过来的请求,打开应用或者是做其他事情。

    如果,知乎应用在后台启动 http 服务,绑定一个端口,比如:12306 吧。那 web 页面可以发送如下的 ajax 请求来实现打开应用:

    $.ajax({
      url: "http://127.0.0.1:12306/open?intent=...",
     }).done(function() {
      // do what you want  
    });
    

    当然,要做的足够细致,还需要实现类似于 ”127.0.0.1:12306/is_inst“ 这样的 api,如果知乎安装了,返回 200,如果服务未启动或者知乎未安装,自然是会返回 404,由此可以在 web 页面中判断是否安装了知乎应用,进而决定是否要显示「打开应用」的按钮。


    通常,必杀技都是有副作用的,如果需要准确的判断是否安装了知乎,就需要这个 http 服务始终存活,否则就没启动和没安装傻傻分不清楚了。至于如何使得 “一个已安装应用在各种情况下都保持后台运行”,则是另一个充满了黑科技的领域,待日后再聊聊这个话题。

    PS:启动后台 http 服务的代码,在豌豆荚 git 中保持 review 不通过的状态估计都得有一年了,因为豌豆荚 Android 应用 “用户不主动开启相关功能则不允许后台常驻” 的原则相悖。所以如果有天你在微信页面中突然打开了某个 “安全” 应用、“搜索” 应用,千万别觉得神奇,而是在看看本文,琢磨一下那是付出什么换来的。


    本文作者: 张楠 ,豌豆荚工程师。目前专注于各种黑科技及 Growth Hacking 手段。欢迎 E-mail 与 (tou) 我 (jian) 交 (li) 流 (ba):zhangnan@wandoujia.com。
    原文:http://zhuanlan.zhihu.com/andlib/19848910#!
     
  • 相关阅读:
    Prim+堆优化
    Tarjan缩点+建新图
    CF482A
    CF545C
    CF570B
    Python 入门2 list介绍
    Python 入门1 上传代码
    黑客与画家 第十三章
    黑客与画家 第十一章
    黑客与画家 第五章
  • 原文地址:https://www.cnblogs.com/gaoxue/p/4385033.html
Copyright © 2020-2023  润新知