360手机卫士插件化RePlugin今日开源

写在前面

“RePlugin将在6月底开源,这将是咱们献给安卓世界最好的礼物。”当咱们宣布这一消息时,心中的激动,无以言表。是的,三年的“厚积”,现在的“薄发”,看似平凡的话,实际上却饱含了咱们太多的激动、辛酸与泪。git

那么今天,咱们就来详细的和您聊一聊,这个从2014年中旬,正式在手机卫士上启用,并即将开源的360 RePlugin,究竟能为咱们,更为您能带来什么。github

GitHub地址:https://github.com/Qihoo360/RePlugin。欢迎您为RePlugin项目加Star、发送Pull Request,提Issue。咱们会竭尽所能回答您们的疑惑。安全

RePlugin是什么

RePlugin是一套完整的、稳定的、适合全面使用的,占坑类插件化方案。其主要优点有:框架

  • 极其灵活:主程序无需升级(无需在Manifest中预埋组件),便可支持新增的四大组件,甚至全新的插件;
  • 很是稳定:Hook点仅有一处(Classloader)。其崩溃率作到仅为“万分之一”,并完美兼容市面上近乎全部的Android ROM;
  • 特性丰富:支持近乎全部在“单品”开发时的特性,包括静态Receiver、Task-Affinity、自定义Theme、进程坑位、AppCompat等;
  • 进程任意:可以让各组件跑在UI、常驻,甚至是“任意坑位进程”;
  • 易于集成:不管插件仍是主程序,只需“数行”就能完成接入;
  • 自由隔离:想隔离就隔离(如不稳定或占资源的插件,易于释放),不想隔离的模块就混用(如各类基础、UI插件,都跑在UI进程内,性能优异);
  • **管理成熟:拥有成熟稳定的“插件管理方案”,支持插件安装、升级、卸载、版本管理,甚至包括进程通信、协议版本、安全校验等
  • 数亿支撑:有360手机卫士庞大用户量作支撑。 
    截止2017年6月底,RePlugin的:
  • 插件数已达102个(其中,核心基础插件57个)。
  • 插件占应用比(指把代码资源铺开,插件占整个应用的比例)高达83%。
  • 年发版次数高达596次(平均每一个工做日发版2-3次)。 
    此外,目前360公司几乎全部的亿级用户量的APP,以及多款主流第三方APP,都采用了RePlugin方案。

图片描述
RePlugin的核心优点ide

图片描述
RePlugin与现有插件化框架的对比组件化

为何要作RePlugin

插件化的好处

在讲述咱们团队为什么要在2013年末,设计一套属于本身的插件化以前,咱们先来简单谈谈,有了插件化方案后,能为咱们带来多大的便利,它究竟解决了什么问题。性能

对于用户而言

  • 一切按需:利用插件化方案,可让您的应用变得“小而精”。只有当用户须要使用某个特定功能时,才能够下载并开启,且能够随时卸载插件。这不只能够减少APK大小、节省流量,还可明显的减小内存、内部存储占用,将更多空间让给珍贵的相片、文档等资料;
  • 随时体验新版:不用去应用市场等到大包升级,用户能够随时体验到新版的应用。如今红极一时的插件化、动态化(RN类)、热更新技术,都或多或少的在围绕此点而展开,可见其对用户带来的巨大价值。

对于开发者而言

  • 发版灵活:不用等市场上线,等用户主动升级,结果错过宝贵的时机。插件化方案可以让您作到“随时发版”,不受“发版窗口期”的限制。甚至可针对不一样地域、不一样用户群、不一样时段来更新,且能够快速验证本身的构想;
  • 组织结构灵活:一旦发版变得足够灵活,则组织结构上就能够由原来的“统一做战”变成“百团做战”,每一个团队都在开发“本身的插件单品”,制定本身的发版计划;
  • 模块思惟:可让团队造成“模块意识”。固然,插件间、插件与宿主间容许有适度的耦合,但不会是“毫无控制”的那种。这让开发者们意识到,咱们之间是“插件间的协定”,而非“同一屋檐下,随便胡来”,迫使团队以全新又合适的方式来开发应用;
  • Android原生优点:和动态化(RN类)不一样,您可使用最熟悉的Java/Kotlin语言,及各类原生API来开发您的插件。这使得应用能和系统更“契合”,充分利用原生的各类优点,且在性能上几乎感觉不到影响。

如上所述,不管是对用户,仍是对开发者而言,使用插件化框架都是大有益处的,理应作到“飞入寻常应用家”。学习

然而,在实际调查过程当中,咱们却发现了一个和这些好处彻底不匹配的奇怪现象。到底是什么呢?插件

既然插件化这么好,为何……

虽然我事先已作好功课,然而在一次技术大会上的调查结果却让人大跌眼镜——在参会的200多位安卓开发者中,仅有不足5%的比例,使用了插件化方案。超过九成的开发者,目前上没有将插件化应用在软件开发之中。设计

实际上,这和咱们在线下观察到的结果基本吻合。结合以前的调查,咱们发现,有三大挑战制约了插件化在Android开发界的普及:

  • 不够稳定:目前有不少比较灵活的插件化框架,虽然支持特性众多,但因Hook点较多,因此不是很是稳定。所以不少大型项目不是很愿意用它们来开发插件,担忧出现应用崩溃、插件没法正常使用等问题。
  • 不够灵活:有一些相对稳定的插件化框架,又存在“不够灵活、自由”的问题,一旦插件有较大改动,如新增Activity、Service、进程等,就须要主程序发版,更不用说能作到“一年前的主程序,无需升级,能够用新插件和组件”。也所以,不少项目的“接入动机”也就大打折扣了。
  • 功能丰富项目专用:目前市面上的插件化方案,大多仅在功能丰富的大型项目中,才被考虑使用,且多用于边缘功能,好比“红包”、“天气”、“摇一摇”等,他们认为只有“非核心”模块,才会考虑作成插件。这也使得插件化的应用范围很是狭窄。

然而,经过咱们多年的实践证实,以上三大挑战,实际上是能够被攻克的。这也是咱们今天要为您介绍的,360手机卫士首款Android开源项目——RePlugin。

既然这么大胆,那么,咱们到底是怎么作到的呢?

咱们是怎么作的

“不够稳定”怎么破?

前文提到,不够稳定的主要缘由是Hook了太多。那么市面上比较灵活的插件化框架,究竟Hook了哪些呢?

图片描述

注意:这里所说的“Hook”是指经过Java反射手段,获取并修改与系统Server等交互的Internal API,来让框架正常工做的行为,如上面所列部分。正常状况下的反射(例如反射类内部本身的字段)不属于Hook。

看似灵活,然而下列三种状况,将颇有可能致使插件甚至应用,完全不能工做:

  • Android升级:既然是内部 API,那么Android天然不会认为是“不能修改的”,一旦系统升级时作了改动,轻则功能不正常,重则直接Crash。例如,有的插件化框架曾遇到在Android 7.0上出现异常,必须升级主程序才能解决的事故,历历在目。
  • ROM修改:比Android升级更可怕的,是第三方ROM对内部API的“各类改”。这个适配难度是可想而知的。例如自定义Resource、自定义WiFiService等形成的“插件化血案”,不一而足。
  • 使用不当:“常在河边站,哪有不湿鞋”,Hook的点多了,一旦对某一点的实现原理理解不透而出了错。结果,前功尽弃不说,还可能出现更严重,且更难以察觉的崩溃事故,细思恐极。

基于上述的状况,咱们团队在2014年初,研究全新占坑插件化框架(注意,此时DroidPlugin类方案尚未出现)时,就定了个“小目标”:让Hook越少越好。通过一次次的研究讨论,最终肯定只Hook一个点:ClassLoader,且要求“坚持到底”,全部改动都是基于此来展开。

对咱们而言,这是里程碑式的决定,即使到如今来看也是如此。

惟一Hook——ClassLoader

图片描述

修改ClassLoader的点其实不难,如上图所示逐步反射便可。然而须要注意的是,这个ClassLoader必定得继承自PathClassLoader,防止Android 7.x因使用addDexPath而有问题。

除此以外,此ClassLoader所在位置也很是稳定。目前来看,从Android 2.1至今都没有发生过位置、名称上的变化,能够长期使用。

关于这一点,咱们以后会有一篇文章来详述,敬请期待。

“不够灵活”怎么破?

前文提到,就目前市面上的插件化框架而言,若作的足够稳定,则多少会失去一些灵活性。对于咱们拥有这么多模块的产品而言,这一样也是不可接受的。

为此,咱们在“坚持一个Hook点”原则的前提下,经过不断创新,最终解决了上述问题。

咱们的核心思路,是2015年之后才开始“老生常谈”的一个词,那就是:坑位。

坑位方案思想

图片描述

  • 非坑位方案:标准的一一对应关系。例如,插件有个XXXActivity,那么主程序则要求必须也有个XXXActivity(名字未必同样)来对应。 
    一旦插件要添加一个新的Activity,则对应的,主程序也必须得添加,不然就没法使用这个Activity。
  • 准坑位方案:有的(如2013年的咱们)会经过Fragment来模拟Activity,从而实现必定程度上的灵活。 
    然而真的遇到大需求,如和其它应用Activity的交互等,就局限百出,很难称得上是完整坑位思想。
  • 坑位方案:能够作到“一对多”的关系。例如插件有个XXXActivity,则运行时,主程序能够将本身的N1ST1对应到这个XXXActivity上。一旦该Activity退出,则N1ST1就“空闲”出来。而当YYYActivity进来后,又会从新占用N1ST1。这样就能够作到一个坑位(如N1ST1)对应多个实体。 
    此外,这个YYYActivity既能够是已有的,也能够是插件新增的。这样不管插件如何升级,主程序均可以不用动,便可支持新的Activity。

固然,咱们当初设计坑位思想(恕我再强调,是2014年初)时,也毫不仅仅针对Activity,而是整个四大组件,甚至到了后期,连Theme、进程、Task-Affinity等都作到了“坑位化”,只不过实现方式各异而已。

因篇幅所限,这里仅以经常使用的Activity来简述。有关更详细的内容,欢迎继续关注咱们的《RePlugin深度剖析》系列文章。

Activity坑位

图片描述

目前市面上的完整坑位方案,Hook的地段能够说是“各有千秋”,从AMS、Instrumentation到ContextImpl都有,并以此让插件变得更灵活。

而咱们的方案和他们有些不一样:除了ClassLoader是Hook的,其他一概不须要。那么咱们到底是如何开启一个插件的Activity呢?

简单来讲,咱们有五个核心步骤:

  • 记录:经过PM.startActivity方法来“记录”到要打开的Activity的名字;
  • 寻找坑:经过一系列流程来找到一个可用的坑位(如N1ST1)并记录;
  • 开启坑:经过系统的startActivity来直接打开这个坑位(注意,此处没有作Hook);
  • 拦截:当系统调到咱们的HostClassLoader(惟一Hook点)时,咱们“拦了一道”,找到此坑位(N1ST1)对应的真正的Activity(XXXActivity);
  • 返回:加载插件并获取这个真正的Activity的Class对象并返回给系统。

其中,PM.startActivity能够由插件/宿主直接调用。若在插件内部,则能够直接经过startActivity方法来打开,更为方便。

固然,这只是核心思路,而每一步咱们都会作各免费催收系统软件种逻辑处理,尤为是“寻找坑”一节,这也是咱们的核心之一。

Activity分层坑位

找坑是有很是多的注意点:

  • 从“分层”上看,则须要支持:各类LaunchMode、全部透明/非透明的Theme、TaskAffinity坑位、进程坑位等,甚至AppCompat的状况也要考虑。
  • 从“管理”**上看,又须要考虑到坑位回收释放,坑位分配,甚至坑位不足时的处理策略等,不一而足。

篇幅所限,之后会写详细介绍,敬请期待。

向完美前进——动态编译方案

经过刚才的叙述,像PM.startActivity等确实可经过一些方法,来让插件“无成本使用”。可是,像Provider的调用(本质是IContentProvider),Service的stopSelf(是final的),以及因涉及坑位分配,而不得不需复写相应方法的Activity等。这里面存在两个矛盾点:

  • 若不Hook,则必需要插件开发者“自行处理”,稍显繁琐,不够完美;
  • 一旦Hook(如尝试Hook AMS、IContentProvider等),又破坏了咱们坚持的“1 Hook原则”,进而担忧将来出现兼容性问题

利弊相间,使人头疼。

针对这个问题,咱们的核心理念是:“毫不在Hook及稳定性上作任何妥协”,转而创新性的作一套“动态编译方案”,力图从“编译期”来解决这个难题。

大致而言,就是把一些咱们认为须要开发者修改的类和方法,借助神奇的JavaAssist来作自动化修改,这样可节省开发者的改动成本,达到想要的效果。

一图以蔽之: 
图片描述

而作到了这一点之后,你会发现,下面的“梦想”就变成了现实:

图片描述

例如,有个名叫“360桌面”的应用,它想把本身变成“插件”跑起来。那么,有了“动态编译方案”,结合“插件类库”和框架的支持,最终的效果是——只需改几行Gradle,就能直接生成一个APK。这个APK:

  • 既能够做为插件直接跑在主程序中;
  • 又能够做为单品直接安装到设备中。

是的,就是这么的神奇!

就这些了?

固然远不止这些。我在曾演示过一段视频,将庞大又复杂的360桌面变成插件,运行在360手机卫士中。

图片描述

试想,一个桌面插件涉及到的功能是“方方面面”的,小到TaskDesription和SO的使用,大到四大组件、Task-Affinity坑位、静态Receiver和进程坑位的处理,都需一一兼顾。因此,要作到这一点,毫不仅仅是前面所说的那几点就能搞定的。

固然,“让360桌面变成插件”,还不是最有意义的。真正让RePlugin变得更有意义的,就是拿咱们的360手机卫士来“开刀”,让数百个——甚至说,近乎一切——的模块,都成为RePlugin的插件,并完美的运行起来。

“功能丰富项目专用”怎么破?

前文提到,之因此“功能丰富项目专用”,主要和目前市面上的插件化方案的定位有关,以致于开发者认为:“插件 = 免安装”、“基础放在主程序里更放心”、“插件开发成本高”等。

然而,仔细分析深层缘由后发现,其实最为核心的缘由,是插件化和相关框架(包括热更新方案等)的“定位”不一样。我知道的有:

  • 组件化:以Atlas/ACDD为表明,官方的定义是“在运行环境中按需地去完成各个bundle的安装,加载类和资源”,以解决大团队协做时的各类难题,提供了热修复能力。其依赖“编译期”较多但很稳定。而大组件的添加,则仍须要主程序发版才能解决,毕竟如官方所述,“组件化 ≠ 插件化”;
  • 动态插件化:以Dynamic-Load-Apk为表明,其目的是解决发版、升级时的问题。大多采用“非占坑”思路,添加新组件时,仍是会要求升级主程序。此类方案较多,且是插件化的“鼻祖”了,值得咱们尊敬与学习;
  • 免安装应用:以DroidPlugin为表明,官方的定义是“能够在无需安装、修改的状况下运行APK文件”。其场景比较相似于“应用分身” 
    此外,此项目是由咱们360公司的手机助手团队研发,在2015年中旬发布。
  • 热修复:以Tinker/Robust为表明,目的是以最小的代价来快速打各类Patch,让应用可以持续更新。其核心优点是“无需重启进程就能打补丁”。一样,新添加的大块功能,则仍是须要升级主程序的,毕竟这不是“热修复”的主要目标。

那么,咱们是这三种目标之一吗?

显然,都不是。那么,咱们的目标到底是什么?

答案:全面插件化

咱们的目标只有一个:全面插件化。

  • 全面插件化:以RePlugin为表明。其目的是“尽量多的让模块变成插件”,并在很稳定的前提下,尽量像开发“单品”那样灵活,并享受插件化方案带来的各类好处。

也就是说,不管是UI、核心业务、合做插件、后台服务,仍是基础功能,均可以变成插件,并在RePlugin框架内稳定又灵活的运行起来。甚至,不只大项目能用,小项目——甚至只是个计算器——均可以使用RePlugin来提高本身的灵活性,并最终实现“插件满天下”的神奇效果。

而这一点,则是咱们,和目前市面上大多数插件化框架的主要差别。

目前卫士插件的现状

目前手机卫士已有的插件,能够分为如下几类,供各App开发者参考:

  • UI插件:如首页(是的,你没看错)、体检、信息流等;
  • 业务插件:如清理、骚扰拦截、悬浮窗等;
  • 合做插件:如程序锁、免费WiFi、安全桌面等;
  • 后台插件:如Push、服务管理、Protobuf等;
  • 基础插件:如安全WebView、分享、定位等、

而这样的插件,咱们有102个。能够想见,一旦这些插件不能用,那么手机卫士瞬间变空壳。三年已过,回头想一想,值得回味。

一块儿创造更高的价值

说到这儿,让我想起了Lody(VirtualApp做者,高中大牛)在一次采访时说过一段话:

“插件化技术的成熟程度虽然在最近几年呈上升趋势,可是整体而言仍然处于初、中级阶段。App沙盒技术的出现就是插件化发展的创新和第一阶段的产物。在将来,我相信不少插件化技术会被更多的应用,若是插件化稳定到了必定的程度,甚至能够颠覆App开发的方式。”

这,其实也是RePlugin的终极价值,那就是——让插件化能“飞入寻常应用家”,作到稳定、灵活、自由,大小项目兼用。

固然,在“全面插件化”甚至“全民插件化”的道路上,咱们还有太多的路要走,而如此庞大又复杂的RePlugin,是十多位研发人员共同努力的成果,且获得了部门领导,和公司技术委员会的大力支持。我相信,RePlugin的开源,是一场新的开始

相关文章
相关标签/搜索