【Android】 插件化原理

什么是插件化?

也叫动态加载技术,应用在不发布新版本的情况下更新,增加或者移除某些功能模块的技术;

对Android来说插件化通常是指将App拆分成多个功能模块,包括一个宿主和多个插件,宿主的形式是APK,插件的形式可以是APK(未安装), zip,Jar, dex;

Android的插件化最主要两个功能:
免安装免修改

区别于组件化:
组件化是将一个App分成多个模块,每个模块都是一个组件(Module),开发的过程中让这些组件相互依赖,并且可以单独开发和调试单个组件,但最终发布的是所有模块合并打包的APK。

插件化可以解决什么问题?

  1. 并发开发
    由于每个插件相对独立,所以可以针对每个插件单独开发和调试,这样可大幅度提高APP开发和迭代速度;

  2. 宿主和插件分开编译
    宿主和插件编译都是独立的,插件App编译完后期再下发到宿主App里,可加快编译速度,提高开发效率;

  3. 动态更新插件
    插件App出问题或者更新新功能时可以直接下发到宿主App里,不用更新发版宿主App;

  4. 按需下载模块
    插件模块可按需下载,减少宿主Apk的大小;

  5. 解决方法数爆炸问题

插件化的发展历史?

从2012年开源的第一款插件化AndroidDynamicLoader以来,到现在已经有十几款优秀的插件化框架;
几款比较常用的插件化框架(按时间顺序):

  1. 2014年3月,dynamic-load-apk (非Hook方案)

  2. 2015年8月,VritualApp (虚拟机技术)

  3. 2015年8月,DroidPlugin (Hook系统AIDL接口方法)—360张勇

  4. 2017年3月,阿里巴巴 Atlas

  5. 2017年6月,360手机卫士 RePlugin(唯一Hook方案)

插件化原理

插件化需要解决的问题

  1. 插件中的类如何加载?

  2. 插件中资源如何加载?

  3. 插件中的四大组件是如何正常运行的?

在这里插入图片描述

宿主加载插件类的四种方式

方式一:dex合并

原理:
ClassLoader 在 findClass的时候总是会在Elements类型的数组dexElements里获取,并且如果两个dex有相同的类和方法,那么将直接返回位于dexElements数组前面的类和方法。

步骤:

  1. 反射获取宿主App ClassLoader的dexElements字段;
  2. 获取插件apkFile,反射获取Element类型的对象dex;
  3. 把dex和宿主dexElements合并一个新的dex数组,替换宿主的dexElements数组;

方式二:为插件创建自己的ClassLoader

原理:
修改LoadedApk的ClassLoader;
具体来说就是利用ActivityThread对于LoadedApk的缓存机制,将含有自定义的ClassLoader的插件信息
LoadedApk添加进mPackages中,进而完成了类的加载过程。

大致步骤:

  1. 获取ActivityThread类中的mPackages对象,该对象是一个map,key为包名,value是LoadedApk;
  2. 通过反射创建插件apk的LoadApk;
  3. 替换插件LoadApk中的ClassLoader;
  4. 将插件LoadApk添加到mPackages对象中。

优点:

  1. 隔离性好,插件之间,插件与宿主之间类加载互不影响;

缺点:

  1. 兼容性差,要适配多个Android版本;
  2. Hook API较多,AMS, PMS;

方式三:修改宿主的ClassLoader

原理:
自定义ClassLoader,并持有每个插件的Classoader,在loadClass的时候通过遍历不同的ClassLoader去查找相应的类。

步骤:

  1. 自定义ClassLoader,并接管系统的ClassLoader;

  2. 插件初始化时创建插件自己的DexClassLoader;

  3. 将插件的ClassLoader 添加到自定义的ClassLoader中。

方式四:反射加载插件的类
步骤:

  1. 获取插件apk;
  2. 通过apk获取dex;
  3. 通过DexClassLoader的loadClass方法获取插件dex的类;
  4. 反射获取方法并调用。

示例:

File apkFile = getFileStreamPath(apkName);
ClassLoader mClassLoader = new DexClassLoader(apkFile.getPath(), dstPath, null, getClassLoader());
Class mClass = mClassLoader.loadClass("com.example.cjl.plugin");
Object object = mClass.newInstance();
Method mGetId = mClass.getMethod("getId");
mGetId.setAccessible(true);
int id = mGetId.invoke(object);

弊端:
1. 工作量大;
2. 插件类在宿主中不存在。

解决方案:
面向接口编程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值