Android插件化进阶——插件化原理和插件管理器(二)
来源:互联网 发布:linux查看用户权限 编辑:程序博客网 时间:2024/06/06 10:50
上一篇文章我们讲解了如何使用 DexClassLoader 来加载插件文件中的类。
Android插件化进阶——插件化原理和插件管理器(一)
这一节我们来具体讲解一下资源文件的加载,并设计一个简单的插件管理器。要了解这方面的内容,首先得看一下 Android 系统中为我们提供了哪些方法来加载资源,下面来看一下图例。
从图中我们可以看出,当资源只有文件名,那么我们通过 AssetManager
就可以直接通过名字加载资源。如果资源有对应的 id,像图片、动画、布局这一类的资源,会生成 ResourceId 的,我们可以通过 Resource 这个类完成对应资源文件加载,最后通过 AssetManager 完成读写。
所以说,资源的加载主要是通过 AssetManager 这个类完成的,是加载资源的核心,Resource 这个类完成一个资源 id 到文件名的映射。
安装到系统的 apk,系统会为他创建资源加载类 Resources 和 AssetManager,所以安装到系统的 APK 可以直接通过 Context
获取到这两个类的引用,从而完成资源加载,但插件 APK 没有被安装,所以这些 APK 文件并没有这两个资源加载类,但这两个资源加载类又是必不可少的,所以需要我们手动创建,我们来看一下市面上比较流行的做法:
从这张图上我们可以看出,核心思想就是为每一个插件分别创建一个 AssetManager 来完成对应插件资源的加载。在插件加载的时候,通过反射将资源文件加入到宿主 AssetManager,让宿主 AssetManager 指定插件的资源加载路径。
所以讲到现在,我们可以分析出来插件化需要处理的核心问题也就是核心技术:
- 处理所有插件 apk 文件中的 Manifest 文件
- 管理宿主 apk 中所有的插件 apk 信息
- 为每个插件 apk 创建对应的类加载起和资源管理器,这是最核心最重要的步骤
下面我们就要通过一个插件管理器来模拟这个过程。我们创建一个PluginInfo
代表我们每个插件。
/** * PluginInfo 插件实体类 * author:张冠之 * time: 2017/8/26 下午7:52 * e-mail: guanzhi.zhang@sojex.cn */public class PluginInfo { //插件实体需要管理的资源,也可以管理很多其他的资源 public DexClassLoader mClassLoader; public AssetManager mAsserManager; public Resources mResources;}
类加载器、资源管理器和 Resources 是一个插件中 apk 中需要管理的最基础的类,当然你还可以添加一些其他的信息来管理,比如插件内部的四大组件等,这里的信息越多,说明你设计的插件化框架需要覆盖的功能面越多,功能就越强大,当然复杂性也越高。
有了插件的实体类,我们就需要一个插件管理器PluginManager
来管理所有的插件实体类。
/** * PluginManager 插件管理器 * author:张冠之 * time: 2017/8/26 下午7:51 * e-mail: guanzhi.zhang@sojex.cn */public class PluginManager { //单例 private static volatile PluginManager mInstance; private static Context context; private static File mOptFile; //插件内存存储数据结构 private static HashMap<String, PluginInfo> mPluginMap; private PluginManager(Context context) { this.context = context; mOptFile = context.getDir("opt", context.MODE_PRIVATE); mPluginMap = new HashMap<>(); } //获取单例对象 public static PluginManager getInstance(Context context) { if (mInstance == null) { synchronized (PluginManager.class) { if (mInstance == null) { mInstance = new PluginManager(context); } } } return mInstance; } //为插件 apk 创建对应的 classLoader private static DexClassLoader createPluginDexClassLoader(String apkPath) { DexClassLoader classLoader = new DexClassLoader(apkPath, mOptFile.getAbsolutePath(), null, null); return classLoader; } //为对应的插件创建AssetManager private static AssetManager createPluginAssetManager(String apkPath) { try { AssetManager assetManager = AssetManager.class.newInstance(); Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class); addAssetPath.invoke(assetManager, apkPath); return assetManager; } catch (Exception e) { e.printStackTrace(); } return null; } //为对应的插件创建 Resources 类 private static Resources createPluginResources(String apkPath) { AssetManager assetManager = createPluginAssetManager(apkPath); Resources superResources = context.getResources();//宿主应用的 Resources 类 Resources pluginResources = new Resources(assetManager, superResources.getDisplayMetrics(), superResources.getConfiguration()); return pluginResources; } //加载插件方法 public static PluginInfo loadApk(String apkPath) { //步骤一 if (mPluginMap.get(apkPath) != null) { return mPluginMap.get(apkPath); } //步骤二 PluginInfo pluginInfo = new PluginInfo(); //步骤三 pluginInfo.mClassLoader = createPluginDexClassLoader(apkPath); //步骤四 pluginInfo.mAsserManager = createPluginAssetManager(apkPath); //步骤五 pluginInfo.mResources = createPluginResources(apkPath); //步骤六 mPluginMap.put(apkPath,pluginInfo); //步骤七 return pluginInfo; }}
这是一个最基础的插件管理器,对一个插件所必须用到的几个加载类进行了管理。这段源码我们通过loadApk()
方法来展开学习。
loadApk(String apkPath)
方法是我们插件管理器的入口方法,当我们创建了单例的PluginManager
类后,调用该方法即可加载指定 apkPath 路径的插件。这里我们通过一个静态的 HashMap 对象管理插件,步骤一中,如果内存中已经加载过该插件,方法将直接返回这个插件的实体类,这里我们保存 map 的 key 值就是 bundle apk 的路径地址。
如果没有加载过这个插件,将新建一个实体类来存储插件的信息。步骤三通过createPluginDexClassLoader()
方法来创建插件的 ClassLoader。
DexClassLoader classLoader = new DexClassLoader(apkPath, mOptFile.getAboslutePath(),null,null);
这块的内容我们在之前的 ClassLoader 中已经讲解过了,这里不再继续重复,只需要铭记 DexClassLoader 是我们动态加载未安装到系统 APK 文件的核心。
步骤四中,通过createPluginAssetManager()
方法来创建插件 AssetManager。
AssetManager assetManager = AssetManager.class.newInstance();Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);addAssetPath.invoke(assetManager, apkPath);
通过反射构造一个 AssetManager 对象,获取对象的addAssetPath
方法,最后将 apkPath 传入该方法,调用方法添加 AssetManager 可管理的资源路径。
步骤五中,通过createPluginResources()
创建插件的 Resources 类。
AssetManager assetManager = createPluginAssetManager(apkPath);Resources superResources = context.getResources();//宿主应用的 Resources 类Resources pluginResources = new Resources(assetManager, superResources.getDisplayMetrics(), superResources.getConfiguration());
Resources 类的创建稍微麻烦点,要先获取插件的 AssetManager ,并通过宿主应用的 Resources 类来构造插件 Resources 类。
最后把创建好的插件实体类存储到 map 中保存,并返回该实体类,完成整个loadApk()
的调用。
以上就是一个简单的插件管理器的实现方式,有兴趣的同学可以对此进行拓展,比如在PluginInfo
中添加其他信息的管理,比如 Activity,Service 的管理。
源码地址
下面的文章将具体通过 Atlas 的源码来深度学习插件化框架的实现原理。
本文部分内容参考于慕课网实战课程「Android 应用发展趋势必备武器 热修复与插件化」,有兴趣的朋友可以付费学习。
插件化实战课程
- Android插件化进阶——插件化原理和插件管理器(二)
- Android插件化进阶——插件化原理和插件管理器(一)
- Android 插件化原理解析——插件加载机制
- Android 插件化原理解析——插件加载机制
- Android 插件化原理解析——插件加载机制
- Android 插件化原理解析——插件加载机制
- 《Android 插件化框架VirtualAPK :(二)原理分析》
- 详述Android插件化原理
- Android插件化开发原理
- Android实现插件化(热加载)和插件加密
- Android 插件化原理解析——Service的插件化
- Android插件化原理解析——ContentProvider的插件化
- Android 插件化原理解析——Service的插件化
- Android插件化原理解析——ContentProvider的插件化
- Android插件化原理解析——ContentProvider的插件化
- Android 插件化开发(二)
- Android插件化开发教程(二)
- Android插件化原理解析——概要
- CFile与CStdioFile的文件读写使用方法详解
- hdu 6068 Classic Quotation
- Java:设置jdk环境变量却一直不生效
- 求两个多边形的交面积(模板)
- 剑指Offer—56—删除链表中重复的节点
- Android插件化进阶——插件化原理和插件管理器(二)
- 转自网络,共勉20170830
- 转自知乎一朋友关于java程序员初级学习的一些见解
- linux 中 ~/. 是什么意思
- UVA10082WERTYU
- WebView的一些坑
- oracle导入dmp文件
- Anaconda安装PyTorch
- (商用版)对对机如何打印自动饿了么外卖订单小票教程