InstantRun是如何工作的

作者: sheepm | 来源:发表于2016-10-01 16:45 被阅读530次

原文地址

Instant Run: How Does it Work?!

Instant Run是Android Studio上可以称之为魔法的一个黑科技,在代码更改时能大大的减少你重新构建部署的时间。之所以称之为黑科技,是因为它的表现实在是太好了,当我们第一次点击run或者debug的时候,可能在时间上和不开启没有区别,但是当我修改代码后重新run的时候,速度之快可能在我来不及看运行设备的时候已经成功的部署完毕。

以一个简单的构建循环流程图开始

构建,部署,安装,app启动,activity启动

Instant Run的目标十分的简单:

尽可能多的移除中间的步骤,剩下的步骤越快越好

在实际使用中意味着:

  • 构建和部署只基于增量改变
  • 不重装app
  • 不重启app
  • 甚至不重启 Activity

Hot, Warm, and Cold Swaps三种概念

Instant Run =增量构建 + Hot, Warm, or Cold swap
Hot swap:代码的增量改变不需要重启一个app甚至是当前的activity就能成功生效,在大部分方法实现的更改上采用的是这种方式
Warm Swap: 在修改可以被生效和看到之前这个当前Activity需要重新启动,一般是在资源的改动上会采取这种方式
Cold Swap: 这个App会重新启动,不是重新安装,一般是在结构的改变时采取,比如一个类的继承接口或者签名改变

当点击Run或者Debug时,步骤是这样的

Manifest被合并并且联合资源文件以及dex打包到apk中
所有的 Manifests 文件会随着应用程序的资源文件被合并并且打包,同样的,你的java源码文件会被编译成字节码然后通过dex过程转化为 .dex 文件也一起打包到APK中。

开启Instant Run功能第一次点击Run或者Debug,Gradle增加了一些额外的tasks

Instrumentation和App Server被注入到debug APK
Bytecode instrumentation(译者注:改变编译器生成的类的字节码)被添加到了 .class 文件中而且一个新的 App Server被注入到app中。除此之外,一个新定义的Application类也被加进去,它注入了一个自定义的classLoader(类加载器)并且会启动前面的App Server。
因此,你的 manifest 文件被修改为使用这个新的Application,如果你已经创建了自己的Application类,那么Instant Run会实现一个代理。
Instant Run启动之后,如果你修改的代码并且重新点击了run或者debug,Instant Run会尽可能的选择一种过程较少的构建过程,当然根据修改的影响程度不同会有三种不同方式,hot swap(热拔插),warm swap(温拔插)以及cold swap(冷拔插)。

在Instant Run使用之前,Android Studio会检测是否存在一个正在运行的App Server并且可以支持Instant Run的socket连接,这也同时能确保这个app正运行在前台,Android Studio能正常工作。

热拔插

如图所示,Android Studio monitors会 针对改变的文件运行一个自定义的Gradle task来生成一个 .dex 文件,Android Studio会提取这些 .dex 文件并且将他们部署到运行的App Server中。
因为原始版本的类都已经存在运行程序中,Gradle会转化 这些更新的类并且使它们有效的覆盖那些已经存在的类。这些转化更新的类会被App Server中的自定义类加载器来加载。
就像下面这张图显示的一样

transformed的说明已经引用摘抄在下面

Starting with 1.5.0-beta1, the Gradle plugin includes a Transform API allowing 3rd party plugins to manipulate compiled class files before they are converted to dex files.(The API existed in 1.4.0-beta2 but it's been completely revamped in 1.5.0-beta1)
The goal of this API is to simplify injecting custom class manipulations without having to deal with tasks, and to offer more flexibility on what is manipulated. The internal code processing (jacoco, progard, multi-dex) have all moved to this new mechanism already in 1.5.0-beta1.
Note: this applies only to the javac/dx code path. Jack does not use this API at the moment.
The API doc is here.
To insert a transform into a build, you simply create a new class implementing one of the Transform
interfaces, and register it with android.registerTransform(theTransform)
or android.registerTransform(theTransform, dependencies).
Important notes:

  • The Dex class is gone. You cannot access it anymore through the variant API (the getter is still there for now but will throw an exception)
  • Transform can only be registered globally which applies them to all the variants. We'll improve this shortly.
  • There's no way to control ordering of the transforms.
    We're looking for feedback on the API. Please file bugs or email us on our adt-dev mailing list.

从现在开始,每一次应用内方法的调用,都会被注入进来的instrumentation和App Server来监听这个方法是否已经更新,如果已经更新,调用会被代理到新“重写”的类上,然后新的版本的方法会替代以前老的方法。

Instant Run Debug
看上面这个动态图中的操作,在修改了 enableLocationUpdates 中的字段后,可以看到黄色圈圈中我们的MainActivity的后面加上了一个 $override 的字样。
修改一个方法的实现能正常的通过热拔插来正常的工作,但是如果是修改了在activity开始创建中的内容呢?

温拔插

温拔插需要重启整个Activity。资源在Activity启动的时候被加载进来,所以当修改他们的时候需要重新启动Activity来强制资源重新加载。
现在任意资源文件的修改都会导致所有的资源被重新打包发送给App,但是Instant Run通过提供一个增量包,用来打包和部署新的或者已经修改的资源文件。

注意:这个温拔插在修改Manifest中的资源文件引用的时候不能生效,因为资源文件的值在APK被安装的时候就已经读取了,当我们改变Manifest文件或者Manifest引用资源的时候,Android Studio还是会执行全部文件的编译和部署。

不幸的是,重启Activity也就是温拔插的方式,并不适用于结构的改变,比如添加,删除或者改变annotations,fields,static和方法实例甚至是改变父类或是静态初始化都会导致第三个阶段,冷拔插。

冷拔插

当应用部署的时候,你的app和子项目会被分割成10个切片,每一个都有自己的dex文件,所有的类根据他们自己的包名被分配到不同的切片中,当进入冷拔插状态时,如果一个类被改变了,那么在重新部署的时候同一切片下的所有的类都需要重新打包成dex文件。
这个方法依赖于Android在运行时加载多个dex文件的功能,这个功能在ART虚拟机上被提出来,这个ART虚拟机虽然在4.4的版本中就已经被加入,但是实际上在5.0开始才真正的取代了Dalvik虚拟机,所以在5.0以下的设备,Android Studio采取的是全部编译部署的形式。

Instant Run的小瑕疵

虽然Instant Run已经足够聪明,能根据不同的场景来采用不同的方式构建,但是有时候代码修改后虽然采取了热拔插的方式,但是一些只有在application第一次启动时初始化的操作并不能影响到,比如下面这个例子,虽然我们已经修改了里面的值,但是通过Instant Run启动的数据并没有变化,这种情况就需要重新启动app来让它生效。

To perform an incremental build and restart the app, click Rerun (CTRL-CMD-r) from the toolbar.

Instant Run提示和小技巧

Instant Run完全由Android Studio来控制,所以在debug时只通过IDE上的start / restart来启动,最好不要直接通过设备来启动,可能

更详细的小技巧清单tips and tricks,这都是Android官方的文档但是只有少量有价值的东西

  • 调整你分配给Gradle进程的资源。在 gradle.properties 文件中合理的设置 jvmargs 参数会显著的提高编译的速度,无论是Instant Run还是全部编译。你可以进行试验并观察构建时间的影响,就能发现这个的价值。
  • 因为ART虚拟机从21版本才开始正式启用,在debug调试的时候将 minSdkVersion 设为21或者更高会充分发挥Instant Run的编译速度。
  • 牢记改变manifest文件会导致全部的编译和部署,速度会很慢,所以,如果你的构建过程会自动的更新manifest的任何部分,比如会自动的改变versionCode和versionName,在debug时最好关掉这个行为。
  • Instant Run现在只作用于主进程,所以如果你的应用使用多进程,热拔插和温拔插在其他的进程会退化成冷拔插的操作甚至是整体全部的编译如果target API在21以下。
  • 如果在Windows下开发,Windows Defender Real-Time Protection可能会导致Instant Run挂掉,可以通过添加白名单列表的方式来解决。
  • 截至该文之前,暂时不支持Jack compiler,Instrumentation Tests,或者同时部署到多台设备上。

相关文章

网友评论

  • please边去:开启 instant run 后。。日志会报一大推 警告信息。。这个怎么解决
    W/art: Failed execv(/system/bin/dex2oat --runtime-arg -classpath --runtime-arg --debuggable --instruction-set=arm64 --instruction-set-features=smp,a53 --runtime-arg -Xrelocate --boot-image=/system/framework/boot.art --runtime-arg -Xms64m --runtime-arg -Xmx512m --compiler-filter=speed --instruction-set-variant=generic --instruction-set-features=default --dex-file=/data/app/com.example.zhaotai.myapplication-1/split_lib_dependencies_apk.apk --oat-file=/data/dalvik-cache/arm64/data@app@com.example.zhaotai.myapplication-1@split_lib_dependencies_apk.apk@classes.dex) because non-0 exit status

    Failed execv(/system/bin/dex2oat --runtime-arg -classpath --runtime-arg --debuggable --instruction-set=arm64 --instruction-set-features=smp,a53 --runtime-arg -Xrelocate --boot-image=/system/framework/boot.art --runtime-arg -Xms64m --runtime-arg -Xmx512m --compiler-filter=speed --instruction-set-variant=generic --instruction-set-features=default --dex-file=/data/app/com.example.zhaotai.myapplication-1/split_lib_slice_0_apk.apk --oat-file=/data/dalvik-cache/arm64/data@app@com.example.zhaotai.myapplication-1@split_lib_slice_0_apk.apk@classes.dex) because non-0 exit status

本文标题:InstantRun是如何工作的

本文链接:https://www.haomeiwen.com/subject/pxqryttx.html