写在前面
今天,我想给大家讲一段故事,这个故事里包含有黑科技、天才少年,有意气风发的豪情和壮志未酬的遗憾。更重要的是,这是一段真实的故事,是我人生中的一段重要经历。
对于互联网人来说,这个冬天格外寒冷。
“辛苦一年半,现在要被联合创始人给踢出局了,技术创业真是悲哀。”一个沉寂已久的微信群里,突然冒出了这样一句话。
我看了一眼发消息的人,备注是“勇哥 创业”,我心里一紧。
群友被这个消息炸出来,有的看热闹,有的义愤填膺,纷纷要求曝光无良公司。我却不由得回想起当初与勇哥结识的故事。
勇哥大名叫张勇,我与他相识是在2015年秋季,当时我正在一个程序员论坛上闲逛,突然一个帖子映入我的眼帘:“搞了个安卓上免安装运行的,准备开源一下”。这个帖子一下子就引起了我的兴趣。
移动开发正是我当时的关注领域,我对智能手机上一切前沿的、好玩的技术都充满好奇。帖子里说,他的这个技术可以通过主App启动任意第三方App,我以前从未听说过这样的技术。
很快,通过朋友介绍,我和帖子的作者张勇搭上了线,他当时是安卓版360手机助手的技术负责人,9月份,我前往酒仙桥360总部,与他见了面。
张勇敦厚面相中透着机灵,和大部分程序员不一样,他十分健谈,说起自己开发的DroidPlugin眼里带着光。和他聊了两个小时,我确信,这是一项安卓开发黑科技。
中国的技术都是业务驱动的,先有需求,然后研究怎么能做到,DroidPlugin诞生的背景也是如此。
14年左右,中国和国外的App理念走上了截然不同的两条道路。在国外,一个App最多做两三件事,但在中国,一个App恨不得装下所有功能,这就是所谓的超级App。
超级App有很多好处,但是,谷歌在设计安卓的时候,没有考虑到存在超级App的情况,在安卓早期版本里,一个App里只允许存在65536个方法,一旦超过就会报错。65536已经很大了,就和千年虫问题一样,开发安卓的工程师们根本没想到有App会需要那么多的方法。
这个问题在国外被Facebook发现了,Facebook的App很大,可能是国外仅有的几个可以称作超级App的应用了,它给出了一个暂时绕过的解决办法。
国内开发者不满足这种暂时绕过的方法,早在2012年,大众点评的工程师图毅敏在研究Android源码的过程中就发现,通过对AndroidDynamicLoader方法的应用,可以做到动态加载资源甚至代码。2014年底,当时在百度的安卓工程师任玉刚开源了Dynamic-load-apk,将这种方法更进一步。
到张勇这里,他把动态代理发展到了极致,让Android系统的四大组件都可以动态加载,这样,安装App的时候只用装一个宿主App或者叫壳App,然后在宿主App里远程下载代表各个功能模块的App就行了。这种技术流派,后来被称为安卓插件化技术。
想象一下,你的手机只用安装一个App,如果想用其它App,点击下载之后就可以运行,省去了麻烦的安装过程,甚至你还可以用不同的账号同时打开一个App,这就是插件化的神奇之处。
安卓插件化的一个变种是组件化技术,它并不用分成不同的App,而是平时各个模块分开开发,发布的时候一起打包,这种技术的集大成者就是手机淘宝研发的Atlas组件化框架,2014年初伯奎对外首次分享,2017年3月正式开源。
超级App还会带来一个问题,就是App的更新,当App由数十个团队,数百上千人开发,版本控制和更新变成了一个很麻烦的事情,特别是线上版本发现bug时需要及时更新版本处理,而国内安卓渠道众多,依赖各个平台更新是不现实的,只有自己处理更新,为了降低更新给用户带来的影响,国内又发明了热更新技术。
早期的热更新技术借鉴了安卓极客最爱的工具Xposed,2015年7月左右,淘宝的白衣开源了安卓切面编程框架Dexposed,它在Xposed的基础上进行改造,使其不需要root就可以任意改变应用的功能。但是,这个技术只支持Dalvik运行时,对于新的ART运行时无能为力。随后不久,支付宝安卓团队推出了AndFix,能很好的支持ART,很快成为阿里系的标准热更新工具。
腾讯这边,QQ空间在2015年分享了它的热补丁技术,它是基于谷歌为了解决方法数溢出而推出的MultiDex方案,第二年微信推出了更好的热更新框架Tinker,也被广大的开发者所喜爱。
2016年,美团的人告诉我,他们在研究了Android Studio 2.0里的Instant Run功能后,推出了Robust热更新框架,成为安卓热更新的一个新的技术流派。
插件化、组件化、热更新,从2015年开始,国内的移动开发技术爆发了井喷式的发展,这些是国内独有的技术,在这一期间涌现出无数开发者,他们抱着极大的热情研究技术并进行开源和分享,那是一个对移动开发者最好的时代。
在这一群人中,我有两个人印象比较深刻。
2016年初,我开始筹办GMTC全球移动技术大会,找张勇推荐讲师,他给我介绍了罗迪。从勇哥的描述里,显然他对罗迪的技术极为认同,然而让我大吃一惊的是,罗迪当时才高二,还是个在校学生。
我一度怀疑他的技术水平,不过在通过邮件和微信交流后,我打消了这种怀疑。他当时已经开源了几个Android插件化方面的工具,其中包括ART运行时的Hook工具Legend,这是当时最前沿的课题。在交流中我也了解到,他对插件化技术发展有深入的洞察,让我十分佩服。
毫无疑问这是一个天才少年。据他自述,他从初二开始自学编程,初三开始学Java和安卓开发,业余时间全部用来学习和研究Android源码,这一点,就连职业的开发者也难以做到。他的天才并不是说具有学习和编程的天赋,而是可以静下心来学习在一般人看起来枯燥的技术。
不过,虽然我认同他的技术,但是在权衡之后我还是放弃了让他当讲师的想法,因为不想拔苗助长。但我邀请他来参加GMTC大会,以及一个安卓的闭门会议,并在闭门会议上做一个分享。
6月24号,GMTC如期举行,我也见到了罗迪,他在微信上很活跃,但在现实中看上去比较木讷,讲一句话需要思考一段时间,不太擅长与人打交道。
他说,插件化今后的方向是沙盒和双开,后者又被称为“分身”,曾有一段时间,各种手机游戏小号、微信分身非常火,就是用的这种技术。
值得一提的是,当年那次闭门会议,几乎囊括了当时在安卓插件化方面研究最前沿的一批人,会议结束后,我请他们吃饭,拍照留念,现在一看,全是回忆。
(GMTC2016 安卓插件化闭门会议合影,后排右二是罗迪)
当时的罗迪已经被市场所发现了,我发布了采访他的文章后,有人专门给我写邮件想让我把罗迪介绍给他,张勇还告诉我有老板专门到北京就是为了看他。
再后来我没有他的消息了,不过,他给我带来了一点体会:当一个行业井喷时,会有这样超出常理的天才涌现。
上面介绍的技术都是安卓平台的,iOS和安卓平台的技术差异很大,像插件化这样的技术不太可能实现。在那几年里,iOS讨论最多的是组件化。
不过,iOS和安卓有一个共同的需求,那就是热更新,和安卓分发渠道太多不同,iOS需要热更新,是因为苹果审核太慢,以及审核容易发生意外,虽然苹果有快速审核通道,但那远远不够。我们需要能绕过苹果审核的更新办法。Bang的JSPatch应运而生。
Bang曾经在百度工作过,后来去了微信读书,JSPatch就是他在这段时间开发出来,并以个人名义开源的。
Bang是潮汕人,2016年我邀请他参加了第一届GMTC大会,在短时间的接触中,感觉似乎比较腼腆,但在网络上,他有一个博客,我很喜欢看他的博文,不仅言之有物,而且能切中要害。
Bang因为JSPatch而名声鹊起,GMTC的时候他的演讲爆满,有人专门过去看他。
JSPatch并不是第一个iOS热更新工具,在之前还有基于Lua的WaxPatch,后来由淘宝的君展维护,但WaxPatch需要带一个Lua运行时会增大体积,而JSPatch则颇为小巧,借助iOS平台内嵌的JS引擎,代码行数长期保持在2000行以下。从2016年起,我了解到的国内大多数头部应用,几乎全部使用了JSPatch,包括互相之间存在竞争的BAT巨头们,在注重门户之见的国内,这实在是个了不起的成就。
然而,正因为JSPatch的流行,当苹果决定收紧审核政策时,JSPatch首当其冲,结果让整个中文互联网几乎都受到了影响,这个下面再谈。
2016年,国内的移动开发技术发展到了最鼎盛的时期。插件化/热更新成为显学,成为高级工程师的必修课。
张勇在乐视最风光的时候去了乐视体育,后来又被人鼓动,以技术入股的形式去做PC安卓模拟器的创业。
360安全卫士的张炅轩等,开发了一个更完美的插件化技术RePlugin,并在2017年的GMTC上开源。
微信发布了Tinker,美团发布了Robust。
聚划算的朴诚发布了LuaView,另一个基于Lua的iOS热更新工具。
刚刚收到苹果投资的滴滴宣布合并Uber中国,它招募了当时iOS领域的大牛Sunny孙源和安卓的任玉刚,开始在移动技术上大展拳脚。过不久,Sunny就推出iOS动态化方案DynamicCocoa,它比JSPatch更加激进,已经有安卓插件化的几分模样;曾鼓捣出Dynamic-load-apk的任玉刚则推出安卓插件化方案VirtualAPK,与RePlugin同台竞技。
QQ还推出了一个号称史上最疯狂的iOS动态化方案OCS,它们开发了一个自己的中间语言OCScript,还开发了一个自己的虚拟机OCSVM去执行它……稍微懂点编程的就知道这是一个多么疯狂的方案。
那的确是一个百花齐放的时代。而身处这个时代甚至参与其中,几乎每天我都活在激动当中。
很多人不知道的是,InfoQ的愿景是推动软件技术发展,这是一个颇显狂妄的说法,技术推动社会发展,而我们要推动技术发展。我将它当作了我的职业信条。在那段时间里,我能感觉到所处领域每天都在往前发展,能感觉到我所作的事情,无论是报道和微信社群,还是线下大会和沙龙,就像拓荒一样,都在一点点的推动这个领域的外延更加扩大。没有比这更好的工作了。
当时我发现一个问题,就是这些黑科技只在国内发展,没有人把它介绍到国外去,国内外之间缺乏交流。于是我给InfoQ英文站的社区编辑Sergio De Simone写了一封邮件,看看有没有可能对国内的技术做一些报道。Sergio是一名软件工程师,业余时间帮InfoQ英文站写了许多技术报道,其中大部分是移动领域的。
然而Sergio的回复让我比较沮丧,他认为这些技术违反苹果和谷歌的规则,不太可能在国外应用,因此报道的兴趣不大。曾经动过想把张勇推荐到国外QCon的心思也熄灭了。
2016年6月的闭门会议上,我号召大家多多在国外网站和社区上推广插件化技术,可惜没人听进去,在我了解的范围内,唯一做过这方面的努力的是LBE的冯森林,他在参加Google IO的时候向国外工程师演示插件化的神奇,据说当时老外惊呆了。后来谷歌推出了自己的免安装应用Instant Apps,不知道是否有受到启发。
现在回想起来,我当时可以做得更多的,即使未必有用,但总得试一试。
然而还没有等我再次鼓起勇气,苹果的打击到来了。
2017年3月,众多iOS开发者收到警告邮件,声称其App违规使用动态方法,责令限时整改。
这封邮件引起了开发者的恐慌,连React Native都遭受池鱼之殃,经过一番寻找之后,发现问题集中出现在两个热更新工具Rollout和JSPatch上,其中Rollout国外用的较多,JSPatch则主要是国内使用。
在当时的分析文章里,该事件的影响一节里我写道:
在国外,本次警告事情其实受影响并没有那么大,国外iOS平台热修复或热更新并不流行,Rollout的声明里,本次只有数百个App、数百万最终用户受到影响。
但在国内,这一数字要远远超出。去年以来,凡是公开分享过iOS应用架构的,都将热修复作为其基础设施之一,可以说大部分头部应用都有使用JSPatch或类似方案。本次受影响的国内App数以千计,覆盖的人群则包括几乎所有中国iOS用户。
更长远的影响是,热修复对一个团队的开发流程和节奏紧密相关,很多团队都必须修改相应的开发流程来适应变化。
这一判断并没有夸张,在苹果警告之后,iOS动态化的工具都转入地下发展,关于这方面的研究和分享也急剧减少,甚至连整个iOS技术的分享也变少了。在另一篇文章里,我写道:苹果的一封邮件像《三体》的智子一样锁死了国内技术。从那以后,“iOS开发没人要了”成为一个梗,流行起来。
在安卓平台,虽然谷歌没有能力像苹果一样干涉国内的开发,但插件化技术从另一方面遭遇了困境。
这一困境就是安卓新版本以及国内各种魔改ROM对于底层的改动。安卓插件化技术依赖部分底层方法以及私有API,而这些在新版本里是很有可能改动的,一旦修改了,插件化就会失效甚至出错。国内各大手机厂商的系统也喜欢对底层进行修改,它们的修改甚至都不会公开告知,因此兼容问题是插件化技术遇到的最大挑战。
2018年发布的Android 9.0,甚至要求开发者不得使用私有API,少了这些API,安卓开发被重新关回笼子里,还能玩的黑科技大大减少,无意之中竟然取得了和苹果警告类似的效果。
在苹果警告之后,我疯狂的阅读网上的报道,希望能看到转机,然而越是读下来,我的心里越是冰凉。
在Hacker News对于事件报道的讨论串里,大部分人对于苹果的行为持赞同态度,原因是隐私和安全。
插件化和热更新对隐私和安全的威胁在于,用户无法控制或得知应用被偷偷的嵌入恶意代码,部分插件化方案要求提前获取所有插件App所需要的权限,这意味着开发者可以利用它来窃取用户的隐私。
而隐私和安全在国外是一个禁区,不可越雷池一步,即使并没有造成实际危害,只是有这方面的风险,相关的技术就不可能被允许。这个,其实Sergio早在一年前就告诉我了,然而我还抱着侥幸心理,并没有重视他的回复。
插件化和热更新的问题就在于它们的能力太强大了,犹如过于锋利的双刃剑,从苹果和谷歌的角度,必须要加以限制。
事实上,插件化已经被拿来做过坏事了,DroidPlugin就曾被黑产利用,在2017年爆发过Triada和TigerEyeing两起病毒木马事件。
至于热更新实际上尚未造成危害,它只是被一家国外安全机构检测到有风险,就遭到了苹果的坚决取缔。但在国内,它已有被滥用的苗头,在苹果警告事件中,有些没有使用热更新的App也收到了警告,后来才发现,有些第三方的SDK使用了JSPatch,而这些第三方开发商做些什么,甚至连App开发者也不能控制!
所以,从这个角度来看,插件化和热更新是需要防止滥用,而在之前,我只看到这些技术好的一面,对于它们的负面影响刻意忽视,违背了媒体中立的准则和监督的职能,现在回想起来,实在是不成熟。
从另一方面思考,我又难掩愤懑之情——苹果和谷歌打击插件化和热修复,实在是太轻易了,并且,从始至终,它们从未与国内开发者有过沟通,从未考虑过国内的特殊情况。
苹果取缔热修复,只需要通过一封邮件,它甚至都不需要修改审核规则,而只需要暗中调整规则的解释:iOS安全大牛蒸米在微博上说,警告中提到的动态方案,其实审核上一直写着不让用,但是实际用了审核也并不会被拒。而现在,它们只需要严格执行规则就行了。
而国内和国外,不仅在对待隐私上的态度不同,App的形态上也有差异,国内的超级App带来的新需求,为什么不能让苹果为国内市场单独推出一些新功能和政策?
我深深的体会到,国内的这些移动开发技术,其实就是沙滩上的城堡,对于操作系统的路径依赖,让苹果和谷歌哪怕做一些小小调整,也足以让这些酷炫的黑科技遭到毁灭性打击。
这段经历让我无比渴望国内出现自主的操作系统,让我深刻理解了自主操作系统的重要性、底层技术和制定规则的重要性。然而我也知道,要做一款主流的操作系统,不仅仅是技术问题, 更重要的是历史的机遇。
更实际些的考虑,要尽量避免类似的事件发生,我们需要尽量加强国内外的技术交流,避免双方的技术差异过大。
2017年6月的第二届GMTC的开场上,我提醒参会者:苹果和谷歌一直在坚定的推动Web技术,在热更新和插件化的道路选择上,我们和国外走得越来越远,这真的是一件好事情吗?我们是否走了弯路?
然而,这已经是马后炮,这些“黑科技”技术的衰落,已不可避免。
对我来说,这篇文章充斥着大量的回忆,格外难以动笔。
对于过去的事情,记忆难免有所美化,有些地方也可能记错,读者如果发现,还请海涵。
这段经历对我的打击甚大,在一段时间内,我甚至对移动技术失去了兴趣,沙滩城堡的意象在脑海中挥之不去,看到新的技术总有一个声音在耳边说:没有用的,只要苹果爸爸稍微改改,这技术就得进历史的垃圾堆……
在做移动开发内容和活动时交了一些朋友,也得罪了一些人,后来大部分也不再联系了……
插件化和热更新技术是真的不可避免的衰落了,它们已经错过了历史机遇期,新的技术已经从另一个维度实施了降维打击,没错,说的就是小程序。
据了解,有些大型App如淘宝等,已经开始用小程序来取代一些原生的功能模块,这一职责正是插件化的范畴,而小程序的热更新相比原生,更加简单和自然。小程序还能成为平台吸引第三方入驻,这在插件化中只有在RePlugin那里有些想法的雏形而已。
更大的改变则来自于行业风向的变化。头条系“App工厂”取得的巨大成功,让人们重新思考App矩阵的价值,人们不再往超级App里加功能,而是又开始开发新的App了,对这些App,插件化基本没有用武之地。
很多移动开发者都转行了,张勇最终还是和老板协商解决了问题,投身到下一份工作里。
Bang去了蚂蚁金服,他还在坚持。在他2018年总结的博文里,他写道:
JSPatch 8月开始遭受另一波审查升级,混淆的方案失效,苹果确实针对JSPatch做了比较厉害的扫描手段并在不断升级,今年跟审核团队沟通他们也是表示不喜欢JSPatch,还是那套审核后不能修改的说辞,就算解决了安全问题也没用,比较无奈,但热修复需求还在,JSPatch平台还是会继续找解决方案。
苹果仍然在赶尽杀绝。
插件化热潮注定成为技术发展的一段小插曲,也许再过几年,不会有人记得了。那段激情飞扬的岁月,终将成为The Wasted Times。
(电影《罗曼蒂克消亡史/The Wasted Times》剧照)
他一直拖到一九四九年五月初才坐上去香港的轮船,算得上真正的末班车。没有人知道他在拖什么或等待什么,我想他自己也未必知道,不过是下意识的拖延。不久他就死在香港,死前再没有值得记述的事件或说过的话,他基本没再说话,这没什么可奇怪的,一切都不值一提,他终于走向自己的沉默。
我很喜欢这段话,觉得感同身受,做过什么,发生什么,到末尾一切都不值一提,然而我终究还是拿起笔,记下那些为了忘却的纪念。
延伸阅读:
搞了个安卓上免安装运行的,准备开源一下
https://www.v2ex.com/t/208960
Dexposed:Android 平台免 Root 无侵入 AOP 框架
https://www.infoq.cn/article/2015/07/dexposed
专访 DroidPlugin 作者张勇:安卓黑科技是怎样炼成的
https://www.infoq.cn/article/2015/09/droidplugin-zhangyong-interview
专访罗迪:高二 Android 大牛的成长之路
https://www.infoq.cn/article/2016/05/lody-interview
JSPatch 开源经验分享
https://www.infoq.cn/article/jspatch-opensource
苹果“热修复门”事件回顾和分析
https://mp.weixin.qq.com/s?__biz=MzUxMzcxMzE5Ng==\u0026amp;mid=2247488291\u0026amp;idx=1\u0026amp;sn=a1ed75c7229ecb01fa3b9e82807b253b
Apple starts rejecting apps with “hot code push” features
https://news.ycombinator.com/item?id=13817557