Freeline和JRebel分析

Freeline原理

Freeline是蚂蚁聚宝团队为Android平台量身定做的HotSwap方案。它的技术思想来源于Buck和LayoutCast,并做了改良。Freeline全版本覆盖,2.x ~ 6.x版本均支持;平台覆盖也比Buck(PC端不支持Windows)和LayoutCast(手机端不支持Android 5.0以下)广。

工作流程:PC端与手机建立TCP长连接,扫描各个子工程文件变化,各个子工程的增量dex构建,增量资源包构建,合并所有工程dex,传输增量包。

增量更新主要分为代码增量和资源增量。

代码增量

在代码增量方面,Freeline采用的是主流Hotpatch的方案:植入Dex 到系统DexList。在Google支持MultiDex后,构建工具默认会按照65536方法及LinearAlloc内存限制进行分包,一般一个大型app,会有多个dex文件存在。系统从dex数组最前的位置开始找,找到对应的Class则不会继续往下找。在应用启动时候,把我们准备好的增量dex通过反射注入到DexElements最前面,则整个增量部署就完成了。

资源增量

资源增量是Freeline相对其他构建方式,比较明显的一个特性。资源增量主要需要解决下面三个问题:

  1. 增量包资源id怎么兼容基线包资源id?
  2. 怎么样高效构建出仅仅包含变更集合的资源包?
  3. 怎么样在手机端让上面构建的增量包生效?

解决第一个问题,Freeline采取的思路是通过最后一次编译res过程的R.java,反向导出保留id所需要的两个文件(public.xml,ids.xml),这个功能抽成单独的工具“id-gen-tool”,该工具会根据枚举常量生成的id的上下文特征,过滤掉枚举常量,解决掉其引起的内存越界问题。
剩下两个问题,可以阅读下面的参考资料,内有详细说明。

缺点

Freeline也并非没有缺点,它有以下几点限制:

  • 第一次增量资源编译的时候可能会有点慢
  • 不支持删除带 id 的资源,否则可能导致 aapt 编译出错
  • 暂不支持抽象类的增量编译
  • 不支持开启 Jack 编译
  • 不支持 Kotlin/Groovy/Scala

Freeline集成

Freeline选择Python+Java作为构建语言,因此需要安装Python环境,并添加到环境变量中。阅读freeline源码,可以发现目前只支持Python 2.7+,对Python 3.0及以上版本不支持。

1
2
3
4
def main():
if sys.version_info > (3, 0):
print('Freeline only support Python 2.7+ now. Please use the correct version of Python for freeline.')
exit()

配置 project-level 的 build.gradle,加入 freeline-gradle 的依赖:

1
2
3
4
5
6
7
8
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.antfortune.freeline:gradle:0.8.4'
}
}

然后,在你的主 module 的 build.gradle 中,应用 freeline 插件的依赖:

1
2
3
4
apply plugin: 'com.antfortune.freeline'
android {
...
}

最后,在命令行执行以下命令来下载 freeline 的 python 和二进制依赖。

Windows[CMD]: gradlew initFreeline
Linux/Mac: ./gradlew initFreeline

Freeline 最快捷的使用方法就是直接安装 Android Studio插件,此外也可以通过python命令

freeline.py -f)```进行构建。因为Closeli项目有多个Product,因此还需要在gradle文件中指定构建的product,比如我们要运行hemu,则要添加如下配置:
1
2
3
4
5
6

```Dos
freeline {
hack true
productFlavor 'product_hemu'
}

在使用Freeline后,freeline 在全量编译后,会自动进行增量编译,但是在以下几种情况下,会从增量编译转入全量编译:

  • 涉及到 build.gradle、settings.gradle、AndroidManifest.xml 文件的改动
  • 增量编译时,发现有超过20个java 文件出现改动

##JRebel for Android原理
JRebel是一款Java热更新插件,最早使用在Java Web以及Java Server端的开发中。它支持所有的JVM语言,比如Java,Groovy以及Scala等;同时支持多个IDE,比如Eclipse,MyEclipse以及 IntelliJ IDEA等。

为了可以在运行期间更新项目代码,需要运行classes文件并在APK中添加代理。代理用于接收变更过的类以及资源。为了减少启动时间,Jrebel在在Gradle Plugin中增加了一个jrebelPrepareDebug任务,但是跳过dexing这个过程。因此构建APK不包括任何.dex文件,但是包括所有相关的APK文件:manifest,resources,.so库等等。一旦这个加壳APK安装完成,所有运行过的项目代码会被发送到代理那里,然后程序就会启动。

如果代码或者资源有变化,点击”Apply changes with JRebel for Android”(安装插件后会出现)按钮,就会启用jrebelCompileDebug这个Gradle task。所有有改变的类和资源会被打包然后通过ADB发送给代理。代理会应用这些变更资源。为了确保app使用的是最新的资源,代理会调用任务栈里top-most activity的recreate方法。通常在设备配置改变后(比如转动设备)才会调用这个方法。

JRebel使用

JRebel For Android的使用非常简单,只要在Plugin里搜索并且安装就可以了。此外它还支持Debuger模式(Freeline不支持)。

使用感受

使用Freeline全量更新Apk,记录数据如下:

次数 1 2 3 4
启动时间 181s 145s 195s 211s

删除了一段代码后重新部署耗时7s,修改资源名后重新部署耗时3s,感觉速度还算快。但是前期集成有点麻烦,而且在使用Freeline后切回AS自带的运行方式,需要先clean一把。
在速度上JRebel和Freeline差不多,但是使用比较简单,而且支持debug模式,综上JRebel更胜一筹。

参考

Freeline项目

Freeline DSL Reference

Freeline - Android平台上的秒级编译方案

Under the hood of JRebel for Android

JRebel for Android