当前位置: 首页 > article >正文

【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. 插件类在宿主中不存在。

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


http://www.kler.cn/a/391486.html

相关文章:

  • 【网络】HTTP 协议
  • Python驱动ansys执行apdl文件
  • Qwen2.5-Coder-32B-Instruct Docker 部署openai接口
  • 网约车管理:规范发展,保障安全与便捷
  • windows tomcat 报错后如何让窗口不闪退
  • 知识竞赛活动中礼仪小姐有哪些工作,要如何安排
  • <AI 学习> 下载 Stable Diffusions via Windows OS
  • 由播客转向个人定制的音频频道(1)平台搭建
  • 【go从零单排】Random Numbers、Number Parsing
  • LeetCode 3242.设计相邻元素求和服务:哈希表
  • rfid工业读卡器怎么跟上位机通信?
  • MyBatis-Plus的IPage分页total不正确问题
  • 【linux】TCP网络编程及Web服务器搭建
  • 利用服务工作线程serviceWorker缓存静态文件css,html,js,图片等的方法,以及更新和删除及版本控制
  • 软件设计师-计算机网络
  • 自适应数据结构、自适应哈希表 (Adaptive Hash Table)详细介绍
  • 私有IP与公网IP
  • 服务器操作
  • 《传统视觉算法在视觉算法中的地位及应用场景
  • SpringBoot(十五)springboot集成Sentinel