Android_N混合编译与对热补丁影响解析

Android_N混合编译与对热补丁影响解析.md

入口文件位于dex2oat.cc中,在这里并不想贴具体的调用函数,简单的描述一下流程:若dex2oat参数中有输入profile文件,会读取profile中的数据。与以往不同的是,这里不仅会根据profile文件来生成base.odex文件,同时还会生成称为app_image的base.art文件。与boot.art类似,base.art文件主要为了加快应用的对“热代码”的加载与缓存。

那么我们就剩下最后一个问题,app image文件是什么时候被加载,并且为什么它会影响热补丁的机制?

###App image文件的加载 在apk启动时我们需要加载应用的oat文件以及可能存在的app image文件,它的大致流程如下:

  1. 通过OpenDexFilesFromOat加载oat时,若app image存在,则通过调用OpenImageSpace函数加载;
  2. 在加载app image文件时,通过UpdateAppImageClassLoadersAndDexCaches函数,将art文件中的dex_cache中dex的所有class插入到ClassTable,同时将method更新到dex_cache;
  3. 在类加载时,使用时ClassLinker::LookupClass会先从ClassTable中去查找,找不到时才会走到DefineClass中。

非常简单的说,app image的作用是记录已经编译好的“热代码”,并且在启动时一次性把它们加载到缓存。预先加载代替用时查找以提升应用的性能,到这里我们终于明白为什么base.art会影响热补丁的机制。

无论是使用插入pathlist还是parent classloader的方式,若补丁修改的class已经存在与app image,它们都是无法通过热补丁更新的。它们在启动app时已经加入到PathClassLoader的ClassTable中,系统在查找类时会直接使用base.apk中的class。

最后我们再来总结一下Android N混合编译运行的整个流程,它就像一个小型生态系统那样和谐。

image

##Android N上热补丁的出路 假设base.art文件在补丁前已经存在,这里存在三种情况:

  1. 补丁修改的类都不app image中;这种情况是最理想的,此时补丁机制依然有效;
  2. 补丁修改的类部分在app image中;这种情况我们只能更新一部分的类,此时是最危险的。一部分类是新的,一部分类是旧的,app可能会出现地址错乱而出现crash。
  3. 补丁修改的类全部在app image中;这种情况只是造成补丁不生效,app并不会因此造成crash。

###运行时替换PathClassLoader方案 事实上,App image中的class是插入到PathClassloader中的ClassTable中。假设我们完全废弃掉PathClassloader,而采用一个新建Classloader来加载后续的所有类,即可达到将cache无用化的效果。

实际代码对应AndroidNClassLoader中的findClass和findLibrary方法,通过调用super.findClass和super.findLibrary来避开调用原本应用PathClassLoader的find方法,原本的findClass方法会先查找classTable造成修复的class无法加载到