Android Studio 2.0 引入了即时编译功能:instant run,一定程度上进行了增量编译、增量更新代码,节省了开发耗时(喝咖啡的时间)。
下面具体分析下instant run相关工作流程和相关的源码
instant run 使用
版本要求
Gradle 2.0 以上
build.gradle:minSdkVersion 15 以上(设置21以上可获得最佳性能)
Android 5.0以上的手机或模拟器
使用
首先确认开启instant run,在settings中搜索instant run,可看到相关设置,默认instant run功能是开启的
当第一次点击 run 按钮 ,进行第一次编译打包。
apk成功安装之后,再观察工具栏,run按钮发生了变化:
然后我们随意修改一部分代码,点击运行,可看到手机屏幕上弹出toast,提示代码已经改变了,可看到最新的运行效果。使用起来也比较方便、快捷。
更新方式
- 热交换 hot swap
更改现有方法的实现代码;不会重新初始化正在运行的app,不要做重启app,activity的操作,即可看到最新代码运行结果 - 温交换 warm swap
更改或移除当前的资源;activity会自动重启(小闪烁),即可看到最新运行结果 - 冷交换 cold swap
对代码有结构性的更改(字段更改、类继承关系、清单文件更改);此时会重启app
参考:
https://developer.android.com/studio/run/index.html?hl=zh-cn
https://medium.com/google-developers/instant-run-how-does-it-work-294a1633367f#.go1u6yq2o
过程分析
第一次打包
instant run 第一次编译打包流程,会执行下面的工作
先来看看生成的apk:
多出了 instant-run.zip文件,那它里面是什么内容呢?
instant-run.zip里的dex文件,是我们真正的业务代码
那instant run 相关的类呢,反而跑到了外层的classes.dex和classes2.dex中。
实际上这2个dex中的内容是instant-run.jar和instant-run-bootstrap.jar 的内容(自己可反编译出来看看):
就是说,第一次运行打包时候,是将 instant-run.jar 和 instant-run-bootstrap.jar 2个jar 变成 2个dex文件,真正的业务代码编译后整合到别的dex中,然后放在了instant-run.zip中
classes.dex -> instant-run.jar instant run 相关api类
classes2.dex -> instant-run-bootstrap.jar AppInfo.class
再来看看清单文件,application 被替换成 BootstrapApplication:
instant run 代码分析
attachBaseContext() 中执行的三个步骤
首先来观察下该类下的 attachBaseContext()方法,其中做了3个比较重要的事情:
createResources() 、setupClassLoaders()、createRealApplication()
- createResources()
主要是判断资源resource.ap是否改变,然后保存resource.ap的路径到externalResourcePath中 - setupClassLoaders()
设置instant run 相关的classLoader,及其继承关系(PathClassLoader -> BootClassLoader 变为 PathClassLoader -> IncrementalClassLoader -> BootClassLoader) - createRealApplication()
进行application 的相关替换,当前app的application变为realApplication;
反射的方式拿到 真实的 Application,通过AppInfo相关字段进行获取
下面我们分析一下setClassLoader详细过程:
主要经历了以下的方法:
这几个ClassLoader类定义的逻辑关系如下:
findClass过程依次委托给 父ClassLoader,最后是让PathClassLoader去加载类
onCreate() 过程
- 通过MonkeyPatcher 替换当前的 application 为 realApplication
包含ActivityThread中相应的Application 都替换成 realApplication - 替换相应的资源resource
替换当前app的assetManager,资源相关的变量等等(期间都是用反射的方式) - Server 创建,建立Socket连接,开启连接
Server 部署工作
在Server 建立起连接后,三种部署工作(hot swap、warm swap、cold swap),都是通过Server进行操作。具体在那种情形下进行哪种交换,源码中有具体实现:
|
|
代码热更新流程
在我们增加一行代码后,点击运行,我们来观察生成的类的变化
在 build 目录下,transforms 中有生成相关的代码
几个重要类
我们来具体看看demo 中代码更改:MainActivity$override类内容的确是最新的代码内容
AppPatchesLoaderImpl类记录了更改的类,存储在一个数组中,供类加载时候,替换成最新的类的代码内容
在此处,我反编译了slice_0-classes.dex:
第一次运行打包生成的 “业务代码” 中,生成的类中的方法里都增加了 IncrementalChange 相关的判断,如果 $change 不为空,说明我们有更改的代码,有更改的代码,则执行最新更改的代码
|
|
最后根据不同的类型,进行相关的重启(activity 或者 app),主要由 Restarter负责,同时也提供了相关的重启方法:
- restartActivity()
- restartApp()
至此,整个 instant run 的分析告一段落,需要慢慢消化一下。。。
源码及工具资源:
https://github.com/fenglincanyi/Study/tree/master/instant%20run%E7%9B%B8%E5%85%B3
参考:
https://github.com/nuptboyzhb/AndroidInstantRun