Apply Changes
对于运行在 Android 8.0 或者更新版本上的设备和虚拟机,Android Studio 现在有三个按钮来控制应用程序重启的程度:
-
Run 会部署所有的改动并重启应用程序。
-
Apply Changes 会尝试应用资源和代码的更改,并只重启 Activity 而不是重启应用程序。
-
Apply Code Changes 会尝试应用代码的更改,而不重启任何东西。
通常只有方法体内部的代码更改才对 Apply Changes 具有兼容性。
原则
基于在 Instant Run 上的经验和反馈,我们采用了一些原则来指导我们的架构设计和决策:
-
将构建/部署的速度和状态丢失两者独立开。我们想将节省构建和部署的时间,与在不丢失运行状态的情况下部署更改这两个目标分开。不管是一般的运行或者调试,或者代码的热替换,快速构建和部署应该是所有部署类型的目标。作为构建 Apply Changes 的一部分,我们发现了很多可以优化构建和部署速度的领域,在后面的文章中,我们会详细介绍它们。
-
稳定性至关重要。即便在 100 次中这个功能以极快的速度运行了 99 次,如果你的 app 因为这个功能而崩溃了一次,并且你花半个小时来尝试找出原因,那么其他 99 次获得的收益也就全部被抵消了。由于我们坚持这一原则,Apply Changes 不会像 Instant Run 那样在构建期间修改你的 APK。带来的副作用是,在我们进行稳定性优化的早期版本中,Apply Changes 会比 Instant Run 的平均速度稍慢,但是我们将继续提高构建和部署的速度。
-
透明。Instant Run 按钮会自动决定是否在必要时重启你的 app 或者 Activity,对于这样不可预测性和行为不一致性的反馈,我们也考虑了进来。我们希望在任何时候你都能清楚透明的了解 Apply Changes 要做什么,如果你的代码有不兼容的修改会发生什么。因此如果有检测到与 Apply Changes 不兼容的修改,我们现在会明确提示你。
架构
我们来深入研究下 Apply Changes 是如何工作的。在你修改 app 之后,当前设备上已经安装或正在运行的应用程序和 Android Studio 刚刚编译出来的应用程序是有差异的,Apply Changes 需要弄清楚如何应用这些差异。这个过程可以分成两个步骤:1)弄清楚差异是什么,2)将差异发送到设备上并应用它。
为了快速确定差异,Apply Changes 没有从设备抓取完整的 APK,而是向设备发送一个快速的请求,去拉取已经安装 APK 的对应目录#Central_directory_file_header)和签名。将这两部分信息和新 APK 进行比较,Apply Changes 可以高效地找出自上次部署以来修改过的文件列表,而不需要检查 APK 的所有内容。需要注意的是,这个算法并不依赖于构建系统,因为差异并不是与上一次构建相比较得到的,而是与安装到设备上的 APK 比较得来。由于 Apply Changes 只针对 APK 之间的差异进行操作,因此它并不要求 Gradle 插件版本和 Gradle 同步。这样,Apply Changes 可以运行于所有的构建系统上。
在生成更改过的文件列表之后,根据所更改的内容,需要执行不同的操作来将这些更改应用到正在运行的 app 上,这也决定了要使这些更改生效,app 需要重启到什么程度:
更改 resource/asset 文件。 这种情况下会重新安装应用程序,但只会重启 Activity,并获取修改后的资源。只有修改过的资源才会被发送到设备上。
更改 .dex 文件。 Android 8.0 的 Android Runtime 提供了替换已加载类的字节码的能力,只要新的字节码不会改变内存中现有对象的布局。这意味着要想兼容 Apply Changes,更改的代码会有一些限制:方法名,类名,和签名都不能更改,它们的成员变量也不能更改。
但这个机制不能在 .dex 级别,只能在类级别工作。否则,如果 .dex 文件包含成千上万个类,即便只有一个类更改了,也需要对所有类进行替换,这样效率太低。对于 .dex 文件,我们比较它的内容来找出更改过的类,只替换掉它们。如果替换成功(例如,类的布局没有改变),为了避免正在运行和已安装的 app 的版本不一致,会在后台安装 app。
更改 .dex 文件和资源文件。 这个情况是上面两种情况的组合。先处理代码的部分,如果成功了,会和新的资源一起安装。为了加载新的资源,主 Activity 会被重启。这是一个全做或全不做的操作,如果代码的改变不能成功地应用,正在运行的 app 什么都不会改变。
更改其他东西。 这是最糟的情况,比如 AndroidManifest.xml 或者 native .so 这些文件被更改了。在这种情况下,是不可能不重启应用程序来应用更改的。“Apply Changes” 和 “Apply Code Changes”这两个操作都不会试图去部署它,它们会告诉用户应用程序需要重启。
关于架构的更多详细信息,请收听 Android Developers Backstage 播客的最新一集,技术负责人 Esteban de la Canal 对 Apply Changes 进行了深入探讨。
比较 .dex 文件
前一个部分解释了 Apply Changes 需要比较并确认在设备上更改(修改/添加/删除)了哪个具体的类。为了不增加从设备获取大量内容的开销,它在后台使用了 D8 的 DEX 文件分析能力来检查 Android Studio 部署到设备上的每个 .dex 文件的内容。根据 .dex 文件中的各个类,计算出校验和,并临时保存在主机的一个缓存数据库中。通过对比新编译的 .dex 文件和之前的 .dex 文件的校验和,Apply Changes 可以在很短的时间内找到更改过的类。
Delta push
学习交流
群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。
35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**