Android 插件化的 过去 现在 未来

2,219 阅读8分钟
原文链接: kymjs.com

写在前面

本文原创,转载请以链接形式注明地址:kymjs.com/code/2016/0…
第一篇文章,作为序文,并没有什么实质性内容,仅仅是一些八卦和历史,重效率的朋友可以选择直接跳过。

过去

三年前,一款名叫【23code】的应用让广大 Android 开发者都”红了眼”,并不是他做的多么酷炫,论内容,他仅仅是一个自定义UI控件集合应用,真正让人捉摸不透的是他的实现,他可以直接下载一个自定义控件的demo,并且运行起来,这是我知道的最早的插件化开发的应用。
当时,Github上有一个开源的插件化框架,叫 AndroidDynamicLoader ,他使用一个Activity作为外壳,动态加载一个未安装apk中的Fragment,并通过外壳Activity来处理Fragment的全部生命周期,达到动态加载一个Android视图界面的效果。 尽管在现在看来这种实现复杂而麻烦,但在当时应该算是很先进的动态加载思路了。他应该是最早的开源 Android 插件化框架。
两年前,在 2014 年 4月,一位中国人开源了一个插件化框架,叫 DL。这个名字是作者自己取的,全名为:dynamic-load-apk。 其实现原理是创建一个静态代理 Activity,将动态加载到未安装的 Activity 中的全部方法(包括生命周期方法)使用静态代理类调用执行。然而由于是动态加载的类,静态代理调用后没有办法拿到super,只能在编写插件时使用that来替代super,而这个that对象实际上就是静态代理类。这应该是最早的国产开源插件化框架了。
同年5月,我在读了DL的全部源码后,完成了自己的第一个插件化框架:CJFrameForAndroid,其实就只是在DL的基础上加入了 Service 的动态加载和Activitylaunchmode 以及插件页面间的数据传递。
同样是 2014年7月(或者8月),一位名叫 null 的朋友(中国人),写了一个插件化加载框架,叫android-pluginmgr 利用 java 虚拟机字节码操作方式,通过动态生成一个插件类的子类,达到插件化的功能。尽管热编译(或者叫热部署)首次创建时效率非常低,但是首次创建后下次加载就能达到比起反射调用快的多的效果,同时可以做到插件代码不必遵循任何限定。这个新的思路让我眼前一亮。然而现在,看到他的master分支已经与 lody 一起,改成了Instrumentation加载,而 dev 分支还保留着我的first commit真的很抱歉,当时答应 null 的到现在也没做到,很遗憾,也感谢你一直保留 dev tree 到现在。
2014年11月,11月份,当时高一刚入学3个月的 lody 也写了一个插件化框架,叫Direct-Load-apk这个库已经被删除了,现在你看到的是一个别人fork的仓库,原因咱们最后讲八卦 ) 这个库我给他起名 DLA,最初跟 lody 说的时候他还闹着跟我争冠名权,明明就是我先说的。DLA使用的应该是当时最先进方案了,以至于第二年的DroidPluginDynamicAPK都采用的是这套方案,只不过实现起来更为严谨。通过找到一切Activity启动的根源:Instrumentation通过替换这个类,来加载一个伪装的Activity欺骗系统的校验,而实际上加载的是未安装的插件Activity。

现在

今年似乎并没有什么新的插件化项目了,所以就从去年说起了。
15年最早出的是阿里的热修复技术:AndFix 通过加载器读取一个dex文件中的类,并找到被注解标记到的要修复的方法,再通过jni在C层替换掉原本出BUG的方法的指针,从而达到热修复的目的。这套方案作为热修复而言并没有任何问题可言,但是热修复毕竟是热修复,不能达到动态添加的目的。
随后还有QQ空间的热修复方案:MultiDex的思路,在应用启动的时候,往ClassloaderPathList里面插入一个Dex,通过覆盖掉出BUG的类来做到热修复。QQ空间只出了理论方案,而这套方案的开源代码实现,则是由 贾吉鑫 写了一个 nuwa 托管在了 Github 上。
相关的热修复项目还有几个就不一一讲解了,咱们继续看下半年出的几个插件化项目。

2015年8月,当时还在360手机助手的张勇开源了一个插件化框架,叫:DroidPlugin 跟上面说的DLA一样,通过修改Instrumentation实现Activity动态加载,通过修改ActivityThread实现Service动态加载。说起来非常容易,但是实际上实现起来应该是困难重重。作者几乎是完全自己实现了一套Framework层中对Service和Activity加载的方案,替换掉了系统几乎全部相关的Binder
同年10月,携程的也开源了其插件化框架:DynamicAPK,内容没细看,据说最成功的是自己实现了一个aapt,替换掉SDK的aapt后可以做到对插件资源重排,达到资源通用。
2016年初开源的一个项目:Small,不光有Android的插件化功能,同时作者还提供了IOS的实现代码。这个项目融合了热修复的类替换思路和插件化的资源加载,是一个非常好的思路。项目的 java 代码本身并没有什么特殊的地方,跟上面讲的差不多,主要内容是在 groovy 脚本里面,通过脚本修改了编译后生成的插件资源id。还没看完,不敢做评价。

未来

说到未来,也不得不提去年出来的ReactNative,尽管我一直说他不可能会成为最终方案,但移动应用web化一定是一个必然的趋势,就好像曾经的桌面应用由C/S到B/S的转变。而怎么web化才是关键之处。
冯老师之前说过,他走的时候已经开始有团队着手研究基于页面级别的动态加载了。什么是基于页面级别,想想今天的浏览器,只有在你真正想要浏览那个页面时,才去加载那个页面的内容。移动应用中的2/8定律,80%的用户访问20%的页面,那么剩下80%的页面是没必要用户去下载的,只有在用到的时候去下载。随着最近重新捡起被我扔掉两年的插件化开发知识,也慢慢感触到这种方案似乎真的是可行的。
再说回RN,呵呵哒,一个HelloWorld项目就是8M+,这什么概念?一个so文件6M什么概念?我敢下断言,抛开阿里的 WeeX 不提,今年下半年绝对还会出基于RN这种思路的web应用框架,而且只会做的更好。
不过这里提一个思路,也是最近团队内部分享的时候想到的,既然RN仅仅是so包很大,他的增量bundle还是很小的,那么有没有可能通过插件化的方案,动态加载这些so包,来解决这个问题?虽然马上就被同事抛来:”那没网怎么办?”这种尴尬的问题。。。

聊点别的

上文讲了一个叫DL的项目,相信如果是了解过的人,应该都清楚,作者叫任玉刚,原点心桌面的后来被百度收购后到了百度卫士,很多人叫他主席因为最初在他QQ群里面等级排行最高的称呼叫主席。任老师写过一篇博客,叫:《树立个人品牌:让名企hr们主动来找你》讲的很在理,原文的点击量已经有15225了。前几天五一去北京,跟廖祜秋 聊到一件事,说现在的技术社区越来越浮躁了。swiftcon刚刚结束,会上某嘉宾打酱油被各种吐槽,一个技术会议居然找了一些辣妹去跳舞,以及直播写代码的美女也做技术分享了。微博上各位大V网红也都开始建立收费群,粉丝红利时代大家都想分到一杯羹。
可是各位想成为网红的朋友,有没有想过在你树立个人品牌的时候,是否有想过拿什么来支撑你的品牌,站在风口猪也能飞,可飞远了以后是没有风了的,需要靠你自己的翅膀了。
最初在写 CJFrameForAndroid 的时候,被某个人威逼利诱过,说项目涉及抄袭,要求加上协作者声明,并在项目主页加上链接,如果这样做了,可以在他火起来了以后,在博客帮我做宣传推广。同样的DLA为什么会删库,据我所知也是有些外力掺和,虽然 lody 并没有跟我说什么。
再说到前几个月,有人利用Github的watchlist,擅自将很多GitHub排名较高的人加入了列表中,从那以后,该项目不管做了什么改动,在列表中的人都会收到一份通知邮件。为此 JakeWharton 还特意回复他让他不要再这么做了。