Instant Run,Android Studio的一个功能,它能在编码、测试或者调试等阶段减少增量修改代码的build和deploy时间。本文会从以下3个方面来阐述:
1. Instant Run的目标是什么
2. Hot Swap,Warm Swap,Cold Swap的概念
3. 当我们点击Run或者Debug时,发生了什么
4. 需要记住的几个要点
一个典型的build周期流程图如下:
Instant Run想达到的目的其实很简单:尽可能多地删除上面的一些步骤,并让其余步骤尽可能的加快速度
说点人话,就是:
- 只build和deploy增量修改
- 不用重装APP
- 不用重启应用
- 甚至都不用重启activity
2、Hot Swap,Warm Swap,Cold Swap的概念:
Hot Swap(热交换,翻译可能不是很准确,大家能意会其意思即可):
增量的修改代码可以在无需重启应用、甚至都无需重启当前activity的情况下生效,函数内部的大多数简单的改动,可以用这种方式。
Warm Swap(这里不翻译,以免产生误解,明白hot 和warm的区别就行,如果不明白,那一定知道热水和温水的区别):
需要重启Activity,新的修改才会体现。通常用于资源改动的场景,比如改了字符串、图标等等
Cold Swap(冷交换):整个APP被重启(像冷启动,但仍然没有重新安装)。用于比如继承或者方法签名这种结构上发生了变化的场景
3、当我们点击run或者debug时,会有以下事情发生:
- Manifest文件跟资源一起合并、并打包成一个APK。同样,java文件被编译成字节码,然后转换成dex文件,也包含在APK中
- 当Instant Run被启用的前提下,第一次点击run或者debug时,Gradle会执行一些附加任务:字节码instrumentation被添加到class文件中,一个新的App Server class被注入到APP里面
- 一个新的Application类定义也被添加,该类定义会注入自定义类加载器,然后会启动App Server。从而,Manifest文件被修改、以确保APP使用它(如果你创建了你自己的Application类,Instant Run会处理你的Application)
那么现在Instant Run正在运行,如果你改了代码,再次点击run或debug,它会使用前面提到的Hot Swap、Warm Swap或者Cold Swap尽可能快捷的完成整个build过程。
注意:Instant Run运行修改之前,Android Studio会检查在一个Instant Run被启用版本的APP里面、有一个连接了APP Server的Socket处于open状态。它主要是确认这个APP正在前台运行,而且它的build ID也是Android Studio所期望的那个版本
我们再来详细看看前面提到的 Hot Swap, Warm Swap和Cold Swap:
1、Hot Swapping(热交换):在我们开发过程中,Android Studio会监控那些被修改的文件,然后运行一个自定义的Gradle任务,只为那些修改过的class生成dex文件。这些新生成的dex文件被Android Studio挑拣出来,deploy到App Server(运行在我们APP里面的那个APP Server)中。由于这些class的原始版本已经存在于正在运行的APP实例中 — Gradle已经转换了被更新过的版本,这样它们就可以有效的覆盖那些之前已存在的class,那些被转换过的、被更新过的class随后又被使用了自定义类加载器的App Server去加载。此时,每当一个函数被调用时(APP内的任何一个地方),注入到我们原始class文件中的工具都会与App Server进行通信,来检查它们是否有更新。如果有更新,执行会被委托给这些新的“override”类,而新的、被修改过的函数就会代替执行。如果你设置断点进行调试,可以在stack trace中看到以“override”命名的类的方法调用。
2、Warm Swapping:warm swap会重启Activity,资源是在Activity启动时加载的,因此,修改资源后,需要重启Activity来强制重新加载资源。目前,对任何资源的修改都会导致所有资源被重新打包、然后传输到APP里面,但Google的Android团队正在开发一个增量打包器(incremental packager),该增量打包器将只打包和deploy新的或者被修改过的资源,这点很期待!
注意:warm swap不适用于manifest本身或者manifest内所引用的资源的修改,因为Manifest里面的值是在安装APK时就被读取的,对manifest(或者manifest引用的资源)的修改会触发全量build和deploy。
遗憾的是,重启Activity也不会带来结构上的变化。添加、移除或修改注解、字段、静态方法或实例方法的签名、修改父类或静态初始化器等,都需要Cold Swap。
3、Cold Swap(冷交换):APP部署后,它和它的子项目会被分成最多10个slice,每个slice都有它自己的dex文件;class基于它们的包名被分配到slice中。在cold swap启用时,一个被修改过的类会要求同一个slice中的所有其他类进行重新dex,之后这个slice才会被deploy到目标设备中。这种方法是依赖于“Android Runtime能够加载多个dex文件”的能力,这是ART引入的一个特性,只有在Android 5.0 (API Level 21)及以上的设备才支持。对于运行API Level 20或更低的目标设备 — 可能使用的是DALVIK runtime,Android Studio部署的是完整APK。代码修改虽然可以通过Hot Swap生效,但是当APP首次运行时,这些修改会影响之前正在运行的初始化器,还是需要重启APP才能使修改生效。
4、关于Instant Run,我们需要记住的几个要点
- 调整分配给Gradle进程的资源:如果你通过修改gradle.properties文件中的jvmargs参数,给Gradle Daemon JVM分配了至少2 gig,那么dex-in-process会被启用,这会明显提高所有build的速度:包括Instant Run、full build、clean build。你需要进行测试、并观察对build时间的影响,以便找到一个比较合适的值
- manifest文件的修改会触发全量build和deploy周期,所以,如果你的build过程会自动更新manifest里面的内容(比如自动迭代versionCode或versionName),那么你可能要在debug的构建参数中禁用该行为
- Instant Run目前只检测主进程,所以如果你的APP有多个进程,在其他进程上的Hot Swap和Warm Swap将会降级为Cold Swap,或者当你的API级别低于21时,会变成全量build
- 如果是在Windows上,Windows Defender Real-Time Protection可能会导致Instant Run变慢,可以把你的项目文件夹添加到Windows Defender的例外情况列表中来规避这个问题
- 目前看,Instant Run不支持Jack编译器、Instrumentation Tests,也不支持同时deploy到多个设备