(译)Instant Run

从官方技术文档的视角来看Instant Run。

本文翻译自Instant Run

目标

Android应用构建工具应当提供更好的代码迭代体验。相对侧重于重新进行完整构建,工具应当以增量方式分发代码变更,可以对这些小的变更进行快速构建、安装和加载。更进一步说,简单的代码变更应当以补丁方式分发和加载到运行中的进程,以避免重新启动整个应用。

概述

  • 提供字节码操作技术用于加载新版本的类文件,加载时不必重新启动运行中的Android应用(称之为hotswap)
  • 提供运行时库用于加载更新后的Android资源(包括图标,布局等等),加载时只需要重新启动Android Activity,不必重新启动运行中的Android应用(称之为warmswap)
  • 对于代码或Android manifest变更导致无法对运行中的进程打补丁的情况,提供基于原始包构建最小差异包的方式来加快分发过程和进程重启过程(称之为coldswap)
  • 最后,提供基于coldswap的增量分发机制用于发布变更到未运行的进程中,而不必重新构建和部署整个APK

运行时

尽管Gradle构建系统可以独立于IDE工作(它可以用命令行调用的方式完成从编译Java代码到打包最终可发布的APK的工作),但instant run还是被设计成集成到IDE中。所以本文中没有关于在命令行中运行instant run的特性描述。

一个小的服务器被内嵌到Android设备上运行的应用中。这个服务器开启端口用于跟Android Studio通信。

当用户在Andriod Studio中修改代码并且重新运行时,Studio调用Gradle构建系统执行增量构建。构建系统会向Studio返回构建结果列表(以下有详细描述)。

hot swap或warm swap场景下,Studio中的instant run client向Android应用中的instant run server发送构建结果,之后instant run server会收到启动类型通知(不用重启,activity重启,进程重启)并并执行对应类型的启动。对于cold swap而言,新的apk通过adb install-multiple -p进行安装(注:有别于完整构建时的安装方式)。

检查器

Android Studio调用Gradle构建后,它并不知道Gradle将产出何种编译结果。InstantRunBuilderContext会跟踪检查verifier状态,这个状态会在构建过程中更新。构建结束后InstantRunBuilderContext将结果写入到构建信息文件(build-info.xml)中,包括所有的构建结果以及verifier状态,而Android Studio会读取构建信息文件(build-info.xml)进行相应部署。

检查器(verifier)可能被设置成以下几种方式:

  • 没有保存任何instant run状态时,设置为INITIAL_BUILD
  • 由Android Studio设置:Android Studio传OptionalCompilationStep.FULL_APK时设置为FULL_BUILD_REQUESTED,传RESTART_ONLY时设置为COLD_SWAP_REQUESTED
  • InstantRunVerifierTransform检查是否可以执行hot swap
  • NoChangesVerifierTransform有两个实例,一个用于检查Java资源(JAVA_RESOURCES_CHANGED),另一个用于检查依赖项目(DEPENDENCY_CHANGED)
  • manifest发生变化时,设置为MANIFEST_FILE_CHANGED。内联到编译后的manifest文件的resource ID发生变化时,设置为BINARY_MANIFEST_FILE_CHANGED

详细的检查状态列表见InstantRunVerifierStatus。根据补丁策略(InstantRunPatchingPolicy,每个检查状态会映射到一个InstantRunBuildMode。比如Java资源变化时,在Lollipop以前的设备上会导致全量编译以及multidex,并使用cold swap方式对multi-apk进行部署。当有多个verifier检查结果时只保存一个,处理方式是将多个verifier检查结果组合成一个。比如HOT_WARMCOLD组合成COLD

如果InstantRunBuildMode如果是HOT_WARM,则接下来的cold swap任务会在PreColdeSwapTask任务中被禁用。如果构建模式是FULLInstantRunBuildContext会将之前的构建结果收集到当前构建中来,所以Android Stuio知道要部署所有构建结果。

Hot swap

Instrmentation

Instant run byte code instrumentation

Hot swap分发

当构建系统确定上次构建后所有代码的变更支持hot swap,它会使用将发生变更的class文件(经过instant run instrmentation处理)打包成reload.dex。Android Studio使用instant run client将reload.dex发送到instant run server(这个server在Android应用中)。instant run server将dex文件写到磁盘,并创建类加载器加载更新后的类。当方法下次被调用时,调用会重定向到新的实现(译者注:从而实现hot swap,Android进程和Activity都不必重启)。

Warm swap

当资源以兼容的变更时,用新的资源生成resources._ap文件

instant run server收到新的资源时会创建新的asset manager(这个asset manager中包含新资源),并通过Java反射用新的asset manager替换掉已有的asset manager。这一过程的具体实现见MonkeyPatcher

(从技术角度讲),warm swap中也可以包含hot swap变更。

Cold swap

当代码的变更超出hot swap可以处理的范围时(比如,增加一个方法或删除一个方法),这些变更被认为是不兼容的。这种场景下会执行cold swap。

cold swap的dexing和packaging任务,会从上次cold swap或全量构建中找到所有的变更(注意:上述hot swap和warm swap构建过程中dexing和packaging任务被禁用)。cold swap为发生变更的class生成split APK文件(发生变更是从上次cold swap构建或全量构建算起)。

Java类在Java package范围内共享,所以如果用户只在一个Java package下修改源文件,则只有一个split APK被创建。

split APK被push到设备上进行安装(译者注:使用的是adb install-multiple -p命令进行非完整安装)。

Android Studio 2.3之前

(译者注:Android Studio 2.3之后instant run的实现原理有改进。目前Android Studio 3.2已发布,所以这一节的内容可以忽略。不过为了保持翻译的完整性,还是放上来)

Android Studio 2.3之前cold swap构建用于生成dex文件,我们使用classloader的黑科技将dex文件添加到应用的classpath中。

dex文件被推送到设备上,它被保存在app数据目录下的inbox中。重启后应用会找到这些dex文件,并用新的覆盖旧的。instant run用新的dex文件创建application classloader,之后使用代理方式调用原先的应用代码。

应用没有运行时会重新构建。接下来跟cold swap的场景是一样的,变更会累积起来直到用户最终部署和运行应用。当执行部署命令时,dex文件首先会拷贝到inbox,接着跟cold swap的流程一样,创建application classloader。在一些设备上,instant run功能不可用,这种情况下只能进行全量构建。