为什么要用插件化

3,797 阅读11分钟
原文链接: www.jianshu.com

作为一个很乱的开头,记录技术选择中的一些思考。

为什么要用插件化?

因为业务的特点。

年前,开始参与「魅族-生活服务」App,一个在O2O领域,不断「整合」用户愿意买单服务的应用。脱胎浏览器的思路,进行了服务到达路径的缩减,算是走在本地化和轻应用的十字路口。

“这应用有啥服务?用户为啥用它?现在大家都喜欢买啥啊?...”,一个个问题,困惑着项目组的每个人,大家都带着疑问和诚意边跑边想,去努力尝试、预测和理解用户的喜好。技术侧,我们也在思考着如何支持快速试错,反馈调整,帮助业务线不断前行?细化分析:

  1. 我们希望业务越来越丰富,但不期望逻辑越来越臃肿;
  2. 我们希望支撑业务快速试错,但不期望频繁升级的叨扰用户;
  3. 我们需要很大程度改变程序架构,但不能停止需求迭代,不能过分冒险,需要综合考虑团队整体的学习曲线;
  4. 。。。

解构了若干考虑点,落实到技术方案:我们期望实现一个支持热更新,可以把业务分拆为可单独试错的插件化服务集,将服务内容化,支持服务运营,在多入口场景式暴露,并不断通过增长黑客的数据思路调优服务重点。

朝着期望,斗志昂扬。短暂蓄力之后,我们在不影响常规迭代路线图的平行方向,开始了插件化的探索与改造。

和插件化的一些故事

最早接触到Android插件化是在网易实习的时候,Mentor大哥一直在捯饬一个插件框架,目标是与支付宝动态业务竞争。大概的想法是,实现一套自定义的UI框架,再使用在线动态Js、UI模板与native交互。想法挺「超前」,无奈当年的JS引擎要么太肥、要么太丑,最终方案很特别的使用了一个隐藏Webview作为JS解析引擎。如果当时有个成熟如Chrome引擎的东西,其精妙细节上和RN确有相似之处。

之后,来到魅族,认识了同组的俊爷,一个挺让人佩服的技术小宅男(单身)。光头出没,还真有技术布道师的风范。他推崇那种:“不好搞定,搞定了就非常爽的方向”(腐男腐女面壁时刻)。先后技术支持了魅族的gradle改造,以及RxJava等框架的普及,都是乍一看很「啰嗦」的东西。

最早认识,这哥们正在用插件化为一个即将客死他乡的项目浇灌最后一点肥料。我一直偏好“自然、直观的实现方式”,于是当一个又瘦又拧巴的小伙成天在整这么一个绕山绕水的玩意时,年少的我是抗拒的。“为啥要应用间传递view,而不能基于一些约定做事?”记得当时摔下这么句话,很鸡贼的站到了看热闹就想事大的队伍,现在特意加粗,用来嘲讽自己。

几年之后,虽然具体细节上和他们陈述的方案有出入,但我最终还是(mei)欢(you)欣(jie)雀(chao)跃的开始了插件化的探索和实践。很大程度上,受到了这些优秀同学的感染。

整个回顾过程,也在重温自己对于技术学习判断的反思:

  1. 我们确实需要使用合理的方式去实现业务,但这个合理不是一味的追求开发上的简单,而应该更多的切入对于业务形态的考量,不然就是智力上的偷懒;
  2. 上层业务开发应该追求简单,但这种简单往往需要底层封装的框架和支撑技术的复杂,不然纯粹的简单和弱基本是一个意思;
  3. 应用开发不应该泛化和局限在常规api的使用。对于底层和周边(例如gradle,gradle,gradle)的了解,常常会让你站在解构技术的角度去创新一些模式;
  4. 不要对不熟悉,不愿意啃的技术说不。也不要停止对于不熟悉领域的了解,更不要对有这种追求的人表示怀疑;
  5. 最重要的一点,技术侧的同学应该关注业务,但是除此之外,应该把关注点从周围那些「乌七八糟」的事情中移开,更纯粹的做些事

启示加反思,我们开始了探索之路。这一道一定是Kubi的,我们不迷信一个技术能通杀全场,也不伸手就问别人要所谓的好,所谓的成型,该填的坑因人而异,别人自然而然的事对自己不一定。

不能再写了,容易暴露取向。

插件化,先做的一些事

技术是业务的一种表达方式。我们做的第一件事,就是理解业务找方向。「生活服务」是一个平行业务集,每种业务有着不同的述求、频次,适合不同的到达方式,等等...

插件化代表一种热更新、分拆的概念,具体实现技术很多。技术没有绝对的好坏,终究用啥是场景和需求驱动。当我们在判定一种技术选型时,我们告诫自己一定不是跟风,跟热,而是关注负责的业务,盯住了那些实现中「不太优雅」的瓶颈环节。技术都是源于需求演进而来,不是凭空架设的空中楼阁。

基于需求拆解,我们需要一种直观、简洁的实插件机制,支持业务快速迭代,多服务可配置,充分试错。同质技术包括:使用之后不再喜欢的Hybrid,红得发黑的ReactNative,吵得耳麻的热更新,还有「百家」争鸣的插件化

  • Hybrid:我们在活动、在原有技术迭代中充分使用了Hybrid,但当本地与H5交互复杂到一定程度时,其实并没有完全的动态;效果真的很差,特别是你把它看做一个App时;
  • RN:作为2015客户端届最大的网红,虽然没被那帮老宅男投资,但是它在差异化展示方面真的很diao,我们会用但仅仅在部分场景去持续使用该技术(生活服务的外放能力环节,我们会重点尝试该技术);
  • 热更新:与插件化很接近,擅长补丁,但多模块插件分拆发力不够;
  • 插件化:大概满足了我们的几个核心期望:
    • 支持热部署、支持多业务自然的分拆。一定是多业务,因为和单业务插件相比,是不同的实现方向;
    • 对于插件的开发者来说,可以做到足够简单,更多的复杂逻辑应该是由框架来handle,更近一步,复杂的逻辑应该在编译阶段处理,特别是资源的分拆;
    • 复用Android API,能在满足动态的同时,提供native的体验。分拆框架维护和插件开发之后,团队多数人短期不会出现陡峭的学习曲线;
  • 可基于原有逻辑进行该着,项目风险较低。

我们慎重的进行了对比和预研,结合服务的落地形式,团队的学习曲线等等因素,最终决定了插件化的方向。

具体到实现,我们罗列对插件诉求的列表和优先级,将关注点集中在那些需要快速验证的风险之上,包括:多插件复杂系统的插件应用效果和优化空间;Flyme os公共主题,控件的使用;多业务分拆与lib,host的版本管理等等。

需求-验证-扩展-再验证,我们以循环推进的方式开始了探索,调研、选型一个接近我们预期的框架,用在线业务进行实现尝试,输出适用性、性能等综合的报告,把一些插件化该填的坑填满,去补齐技术盲点,再进一步决定插件的扩展或定义。

一番选择之后,我们非常高兴与Small相遇在航程的起点。

Small

世界那么大,组件那么小。Small,做最轻巧的跨平台插件化框架。

Small出现于插件化进化过程中,吸取了很多优点的框架。编译多做,运行时少做,我们在这个最大述求上不谋而合,给予了技术选型启示和鼓励。

Small总体可以分为两块:编译环境+运行时框架。对于多数框架,发力点都在于运行时,期望通过android中的ClassLoader、AssetManager等,借助反射关联Android的一些“技术后门”,这些是插件化的基础,大伙通用的方式。但是Small和我们的关注点的则是在编译环节:

  • 期望最大化的资源重用,并合理分配lib:如果host有,则plugin可以共享;如果libPlugin有,则plugin可以映射共享等等;
  • 期望最大程度的优化动态加载的环节:动态加载的优化方式很多,可以对反射进行缓存,也可以借助一些周边方法组合数据,但是我们期望的是利用编译生产一些约定好的数据和能力,支持运行时的最快判断;
  • 期望编译环节与加载环节形成闭环,结合加壳方式,确保插件的安全。
  • 其他的一堆布拉布拉...

Small有诸多优点,也引起了我们思考,但业务落地使用的过程,确是一条艰辛的弯路。因为插件需要handle问题,除了通用的实现原理,存在着性能和覆盖点上的差异。这样的调整需要业务和框架设计思路的双向适配,往往没有好坏,只是初衷不同:

  • 在资源和Lib分拆上,Small开了个好头,但是强制要求host关联lib最大集的方式,是我们的首要分歧。我们希望支持资源、lib的深度差分和插件隔离:这是正在执行的一个优化方案,利用了很多我们认为巧妙的方式,后续希望分享出来;
  • 支持魅族的颜色主题,这其实是Small里面资源索引计算的一个bug(具体细节需要对Android打包中的资源处理补上几课,也是我们早期的一道大砍,不过好在我们挺过来了);
  • 需要解决插件和Lib插件的版本管理和依赖归属问题,这个问题和热部署配合时,需要制定相对复杂的规则;
  • 其他的一些业务扩展,例如Service和Provider,这些都很简单;

当前,我们期望从Small出发,去「任性」地适配业务场景,发现,填满各种问题。

后续我们应该做点什么?

开源是这个时代的礼物。Small发展在各种开源的框架的演进过程中,它的开源又很大程度的激发和鼓励了我们坚持的方向,我们每天的学习和推进中,我们也在不断参考更多的框架。

期望着与Small同行,见招拆招,用业务去验证,去帮助公司更多有相似诉求的。期望着积淀一段时间之后,能反向作用到Small框架,参与框架的优化和演进(当然要和开发者进一步沟通)。

Small在演进,我们落地的方案也在不断成熟,但是因为资源、依赖分拆逻辑的差异性,似乎在走向两个方向。或者有一天顺应着我们的业务发展,我们写了一个与原作初心不同的东西,那么希望大家可以多一个选择,以平行的方式去推进变化。

好在一切都在继续,我们并没有着急的结论。

题图:纪念碑谷的草图,原图信息暂时不详。代表一种循环演进的启示,也作为一种起步阶段的标注。