5

前言
众所周知,微信微博等应用为了防止用户在使用过程当中跳出程序本身,对浏览器里面的自定义协议打开做了限制。这个需求很正常,是个产品经理都能想到。

但作为微博微信这种体量的应用来说,我要多说几句:“右上角使用浏览器打开”成了每个前端开发者的标配,可以说浪费了多少的人力和财力,亦可以说造就了多少的就业机会。APP做得越大,越应该有社会责任感。这种简单粗暴的方式让千千万万的开发者感到心痛,无语。请把选择权交还给用户,既然用户点开了浏览器,我们也理解你们为自己APP操碎了心,是否可以做成弹框再让用户选择:是否打开xx应用?

现状
H5页面增加自定义协议拉起APP在移动端来讲是个非常特色的交互,而且也可以极大提高用户体验,基本是每个应用的标配。通过

<a href="orpheus://playlist/19850925" />

就可以在点击的时候触发拉起应用(前提是系统已经安装支持这个协议的应用,这个例子里面叫orpheus。

微信微博浏览器如何封禁自定义协议呢?

public boolean shouldOverrideUrlLoading(WebView view, String url) 

Android的WebView控件有个叫shouldOverrideUrlLoading的回调,任何链接的打开(包括http和各种自定义协议)都会先走这个回调,交给应用本身来判断是否要接手处理,return true表示接手,WebView就不再处理,否则WebView就继续处理。
处理自定义协议其实不是WebView自带的功能,而是需要开发者在这个回调里面去实现。比如类似这样:

Intent intent = new Intent(Intent.ACTION_VIEW, uri);
if (intent.resolveActivity(getPackageManager()) != null) {
    startActivity(intent);
}

那微信微博与其说拦截,倒不如说他们只判断了自己白名单的协议然后放行。

方法
除了苦逼的前端开发者实现个右上角用浏览器打开功能,还有一些思路可以参考。
1.伪装成白名单协议
我们知道微信有白名单,比如合作的京东就可以直接打开,所以我们可以把我们的应用做成京东的协议名称,这样如果用户手机上面没有安装京东,就能直接拉起你的应用。如果安装了京东,则会跳出弹框让用户选用哪个打开(这个是系统行为)。这个方案有点猥琐。
2.用右上角浏览器打开的时候可以把自己应用置顶。我们知道右上角用浏览器打开其实也是微信调用了上述那段代码,只不过协议固定是http或者https。当然微信还是做过修改的,第一个位置固定放QQ浏览器(即使没有安装)。除了第一个位置剩余是按系统读取的顺序排列的。因为Intent其实是有优先级的,那如何利用系统Intent优先级来让自己的应用在这个弹框里面排在第二位呢?

首先应用当然要实现支持http/https协议,不然你都不会出现在这个页面里面。然后给Intent配置域名信息,如下:
这样在自己域名匹配上面排列就会靠前。甚至你可以在这个协议实现上面,直接将http/https的url转换成native参数,直接打开native页面,省去中间webview跳转。
如果url转成native参数有点麻烦,你可以在分享链接的时候带上Intent.toUri作为参数,这样后续收到url之后直接Intent.parseUri得到Intent,跳转到指定页面就简单了。当然这种做法有限制,比如参数不能缺失,也不能支持其他平台分享过来的链接。如果应用的不同平台能统一跳转协议也是很不错的。

突破
还能再做点什么吗?答案是肯定的。这个突破不是绝对的,而必须要求程序在后台有个活着的进程。如果你感兴趣请继续往下看。

首先我们知道系统的很多弹框都是用Activity实现的,他可以弹出在任何应用的任意一个界面上面,说到底这个弹框也是属于系统的某个应用,比如手机管家等等。那为什么一个APP在后台能启动的一个Activity可以在任意另外一个APP上面呢?
秘密就在于Intent.FLAG_ACTIVITY_NEW_TASK这个flag,我们知道如果context.startActivity里面的context不是Activity,是必须要加这个flag才能启动成功。
第一个问题解决了,在android上面是有可能让我们的一个页面呈现在微信之上。那如何触发调用呢?聪明的你可能想到了,对,就是在H5页面上面给自己应用发请求。
在自己应用里面实现一个非常简单的HttpServer,接听来自本机的请求,一旦收到请求则直接使用Intent.FLAG_ACTIVITY_NEW_TASK拉起Activity。

可惜是当你去尝试在H5页面里面给127.0.0.1或者localhost发请求的时候发现根本收不到,因为狡猾的微信微博浏览器限制了这两个本机地址,限制方式也很简单,还是刚才的shouldOverrideUrlLoading判断下url的host是不是这2个就可以了。于是聪明的你肯定又想到了绕过的方式。没错随便弄个域名解析到127.0.0.1上面。

于是整个链路快要完成了,剩下就是一些额外的工作,比如客户端和H5页面约定一串端口,按照约定顺序依次遍历直到请求得到响应。因为端口有可能被占用。然后再约定一个加解密方式以防止被恶意攻击。

这种方式有没有可能被微信微博封禁呢?因为原理都是系统实现,唯一的可能点就是对域名进行解析发现还是127.0.0.1的话继续拦截。但这个做法有点代价,首先shouldOverrideUrlLoading不能阻塞调用,这样就影响正常使用了。如果做成异步的就需要新增一个dns查询服务,这个完全看xx产品经理的意思。可能原来不会考虑,这篇文章发出去了用的人多了估计会考虑了。。。请给开发者留一条活路。

写到这里我猜你肯定在想,程序活着这个条件是个硬伤,虽然多迈出了一步但还是受限不少。于是又引出了一个古老的话题:如何程序保活。这不是本篇文章的范畴,我也不推荐你去研究和花费心思,这也不是一个正确的方向。
最后给一个提示:你的公司有没有兄弟产品,亦或是有没有使用一个公共的SDK呢?

更多细节请参考我给的例子,点击这里链接可以参看。有任何问题欢迎留言讨论,喜欢就点赞,请支持原创。

更多文章请关注微信公众号:安卓之美


想飞的鱼
226 声望32 粉丝