插件化&热修复

Q:Android中的ClassLoader?

  • 系统类加载器
    • BootClassLoader
      • java实现,包访问修饰符
    • PathClassLoader
      • PathClassLoader来加载系统类和应用程序类
        • PathClassLoader通常用来加载已经安装的apk的dex文件(安装的apk的dex文件在/data/dalvik-cache)
    • DexClassLoader
      • DexClassLoader可以加载dex文件和包含dex文件的压缩文件(比如jar和apk文件),不管加载那种文件,最终都是加载dex文件
        • dexPath:dex相关文件的路径集合,多个文件用路径分割符分割,默认的文件分割符为 “:”
        • optimizedDirectory:解压的dex文件储存的路径,这个路径必须是一个内部储存路径,一般情况下使用当钱应用程序的私有路径/data/data/Package Name/…
        • librarySearchPath:包含C++库的路径集合,多个路径用文件分割符分割,可以为null
        • parent:父加载器
  • 自定义加载器

Q:Android中ClassLoader加载过程?

  1. 新建DexPathList对象pathList
  2. 调用pathList.findClass
    • 新建dexElements
    • 遍历dexElements,调用element.findClass方法
    • 获取DexFile对象dexFile,调用dexFile.findClass方法

Q:热修复方案?

  • 底层替换方案
    • System.load:可以加载自定义路径下的so
    • System.loadLibaray:用来加载已经安装APK中的so
  • 类加载方案
    • 在element.findClass时加载bugfix的包 image
    • 给每个类中的方法插入一个public static的ChangeQuickRedirect对象对所有方法使用Javassist插入代码:当该方法的changeQuickRedirect不为空时,直接将参数直接传入PatchProxy的accessDispatchVoid/accessDispatch方法并返回, 这样做跳过了原方法后面的代码
  • Instant Run方案
    • 创建新的AssetManager,并通过反射调用addAssetPath方法加载外部资源,这样新建的AssetManager就包含了外部资源
    • 将AssetManager类型的mAsset字段的引用全部替换为新创建的AssetManager,主要是Resources中

Q:插件化需要的技术

  • Hook技术
    • 用来启动指定组件,一般hook点为Instrumentation中的execStartActivity
    • 通过静态代理ProxyInstrumentation获取到Instrumentation对象
    • 通过反射执行execStartActivity的方法
    • 通过反射将Activity中的Instrumentation对象替换为ProxyInstrumentation
  • 插件的类加载
    • 获取插件apk地址
    • 传入插件apk地址新建插件类加载DexClassLoader对象
    • 通过反射dexElements获取插件中的类
    • 通过反射dexElements获取当前应用中的类
    • 创建新的dexElements数组合并两个中的类
    • 用新的数组替换应用dexElements中的数组
  • 插件的资源加载
    • 通过addAssetPath加载外部资源,再构建新的Resource替换Application中的Resource
  • 启动插件Activity
    • 使用占坑的Activity通过AMS中对AndroidManifest.xml的验证
      • hook execStartActivity时将intent替换为跳转到占坑Activity的intent,其中原始跳转intent存储在占坑intent中
      • hook ActivityThread中的mH,读取占坑intent中的原始intent,通过setCompnent加载原始intent

Q:Android打包流程

  1. 打包资源文件 资源文件(res文件夹下的文件)通过 AAPT(Android Asset Packaging Tool)打包生成R.java类(资源索引表)以及.arsc资源文件。 经过aapt生成的R文件占4个字节
public static final int design_appbar_state_list_animator=0x7f020000;
  • 第一位字节0x7f表示packageID,用来限定资源的来源。系统资源包是ox01,SharedLibrary类型资源包是0x00,普通App包则是0x7f;
  • 次一位字节02表示typeID,用来表示资源类型,如drawable、layouts、anims、color、menu等;
  • 后2字节0000表示EvtryID,指的是每一个资源在对应的TypID中出现的顺序。
  • aapt生成的.arsc资源文件对应我们将apk解压(apk本质是一个zip压缩包)得到的Resources.arsc,它实际上就是App的资源索引表。简单来说,通过R.java文件与Resources.arsc就可以定位到资源的内存地址
  1. 处理 aidl files 如果有aidl文件,会通过aidl工具(源碼位于system/tools/aidl)打包成java接口类
  2. 编译(Compilers) R.java+工程源码+aidl.java通过javac生成.class文件
  3. dex(生成dex文件) 源码.class文件和第三方jar或者library通过dx工具打包成dex文件。AndroidStudio有提供 proguard、D8、R8等工具来处理这一流程。Android 还会针对 Dalvik 虚拟机和 Art 虚拟机对dex进行优化
  4. apkbuilder(生成未签名apk) apkbuilder工具会将所有没有编译的资源、.arsc资源、.dex文件打包到一个完成apk文件中
  5. Jarsigner(签名) jarsigner工具会对未签名的apk验证签名。得到一个签名后的apk(signed.apk)
  6. zipalign(对齐) zipAlign工具对6中的signed.apk进行对齐处理,所谓对齐,主要过程是将APK包中所有的资源文件距离文件起始偏移为4字节整数倍,这样通过内存映射访问apk文件时的速度会更快。对齐的作用主要是为了减少运行时内存的使用 image image