Android插件化(一、插件化基本原理)

插件化简介

插件化概念

插件化开发就是将整个app拆分成很多模块,每个模块都是一个apk,最终打包的时候将宿主apk和插件apk分开打包,插件apk通过动态下发到宿主apk。

插件化优点

  • 宿主和插件分开编译
  • 可并发开发,都是apk,开发互不影响(宿主需要给插件下发一个context)
  • 动态更新插件
  • 按需下载模块
  • 可解决方法数超过65536的问题

插件化难点

插件化需要解决的问题

  • 插件中代码的加载与主工程的相互调用
  • 插件中资源的加载与主工程的相互访问
  • 四大组件生命周期的管理

解决方法

插件中代码的加载与主工程的相互调用

  • 类加载
    在这里插入图片描述
    Android中对于外部apk的类加载有两种常用的类加载器,DexClassLoader和PathClassLoader,他们都继承自BaseDexClassLoader区别在于调用父类构造器时,DexClassLoader多传了一个optimizedDirectory 参数,这个目录必须是内部存储路径,用来缓存系统创建的dex文件,我们可以用DexClassLoader去加载外部apk,用法如下
    在这里插入图片描述

  • 双亲委派机制
    在这里插入图片描述
    在这里插入图片描述
    ClassLoader在加载类时,先查看自身是否已经加载过该类,如果没有加载过会首先让父加载器去加载,如果父加载器无法加载该类时,该类才会调用自身的findClass方法去加载,该机制很大程度上避免了类的重复加载。

  • ClassLoader的加载过程
    在这里插入图片描述
    在这里插入图片描述
    DexClassLoader重载了findClassLoader方法,在加载类时会调用其内部的DexPathList去加载,
    DexPathList是在构造DexClassLoader时生成的,其内部包含了DexFile。DexPathList的loadClass()会去遍历DexFile知道找到需要加载的类。

  • 插件中的类加载
    通过给插件生成相应的DexClassLoader便可以访问其中的类,有两种处理方式,单DexClassLoader和多DexClassLoader
    互相调用:
    插件调用主工程
    在构造插件的ClassLoader时会传入主工程的ClassLoader作为父加载器,所以插件可以直接通过类名引用主工程的类。
    主工程调用插件:
    若使用多classLoader机制,主工程引用插件的类需要先通过插件的ClassLoader加载该类,再通过反射调用其方法,插件化框架一般会通过统一的入口去管理对各个插件中类的访问,并且做一定限制。
    若使用单ClassLoader机制,主工程可以直接通过类名去访问插件中的类,该方式有一个问题,若两个不同插件工程引用了同一个库的不同版本,程序可能会报错,所以一定要通过一些规范去避免该情况的发生。

  • 资源加载
    Android系统通过Resource对象加载资源,该对象生成过程如下:
    在这里插入图片描述
    因此,只需要将插件apk的路径加入到AssetManager中,就可以实现对插件资源的访问。但是AssetManager并不是一个public的类,因此实现时需要用反射去创建。
    和代码加载过程相似,插件和主工程的资源关系也有两种处理方式。
    ·合并式:addAssetPath的时候加入所有插件和主工程路径
    ·独立式:各个插件只添加自己的路径
    注:合并式由于AssetManager中加入了主工程和所有插件的路径,生成的Resource可以访问主工程和插件的所有资源,但是由于插件和主工程都是独立编译的,所以生成的资源ID会存在相同的情况,就会出现资源访问冲突的问题。
    独立式各个插件资源都是互相隔离的,如果想实现资源的共享,就必须拿到对应的Resource对象
    ·资源冲突
    合并式的资源处理,会引入资源冲突。资源 id 是由 8 位 16 进制数表示,表示为 0xPPTTNNNN。PP 段用来区分包空间,默认只区分了应用资源和系统资源,TT 段为资源类型,NNNN 段在同一个 APK 中从 0000 递增。
    所以思路是修改id的PP段,不同的插件使用不同的PP段,从而区分不同插件资源,具体实现方式有两种:
    1.修改aapt源码,编译期修改PP段
    2.修改resources.arsc,该文件列出了资源id到具体资源路径的映射
    推荐第二种,不会入侵原有的编译流程。

  • 四大组件支持
    Android开发中有一些特殊的类,是由系统创建的,并且由系统管理生命周期,如常用的四大组件,activity,service,broadcastReceiver和contentProvider,仅仅构造出这些类的实例是没有用的,还需要管理组件的生命周期。其中以activity的最为复杂,不同的框架有不同的方式,大致分为两种方式
    1.ProxyActivity代理
    2.hook方式
    hook启动插件activity需要解决两个问题:
    ·插件Activity没有在Manifest文件中注册,如何绕过检测
    ·如何构造Activity实例,并同步生命周期
    解决方法:
    ·先在manifest中预埋StupActivity,启动时hook住,将intent替换成StubActivity
    ·通过插件的ClassLoader反射创建插件activity
    ·之后Activity的所有生命周期都会回调给插件Activity
    Service:Service 和 Activity 的差别在于,Activity 的生命周期是由用户交互决定的,而 Service 的生命周期是我们通过代码主动调用的,且 Service 实例和 manifest 中注册的是一一对应的。实现 Service 插件化的思路是通过在 manifest 中预埋StubService,hook 系统 startService 等调用替换启动的 Service,之后在 StubService 中创建插件 Service,并手动管理其生命周期。
    BroadCastReceiver:解析插件的 manifest,将静态注册的广播转为动态注册。
    ContentProvider:类似于 Service 的方式,对插件 ContentProvider 的所有调用都会通过一个在 manifest 中占坑的ContentProvider 分发。


版权声明:本文为qq_36390114原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
THE END
< <上一篇
下一篇>>