Android 插件化“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 )
Posted 韩曙亮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 插件化“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 )相关的知识,希望对你有一定的参考价值。
【Android 插件化】系列博客 :
- 【Android 插件化】插件化简介 ( 组件化与插件化 )
- 【Android 插件化】插件化原理 ( JVM 内存数据 | 类加载流程 )
- 【Android 插件化】插件化原理 ( 类加载器 )
- 【Android 插件化】“ 插桩式 “ 插件化框架 ( 原理与实现思路 )
参考 【Android 插件化】“ 插桩式 “ 插件化框架 ( 原理与实现思路 ) 中给出的实现思路 , 逐步实现 “ 插桩式 “ 插件化框架 ;
一、创建核心依赖库
创建 " android Library " 依赖库 , 作为 " 插件化 " 框架 核心依赖库 ;
" 宿主 " 模块 应用 , 依赖该 “ 插桩式 “ 插件化框架 核心库 , 依靠该框架核心库 , 管理 " 插件 " 模块 编译打包成的 apk 文件 ;
二、创建类加载器
创建 DexClassLoader , 使用其构造函数创建 , 需要传入四个参数到构造函数中 ;
package dalvik.system;
import java.io.File;
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {
super((String)null, (File)null, (String)null, (ClassLoader)null);
throw new RuntimeException("Stub!");
}
}
DexClassLoader 构造函数 参数说明 :
① String dexPath : 插件包加载路径 ;
② String optimizedDirectory : 开发者指定的 apk 插件包解压后的缓存路径 ;
③ String librarySearchPath : 函数库的搜索路径 , 可设置为空 , 忽略 ;
④ ClassLoader parent : DexClassLoader 加载器的父类加载器 ;
创建插件包解压后的缓存路径 : 注意 String optimizedDirectory 参数对应的路径必须是私有的 ;
// DexClassLoader 的 optimizedDirectory 操作目录必须是私有的
// ( 模式必须是 Context.MODE_PRIVATE )
File optimizedDirectory = context.getDir("plugin", Context.MODE_PRIVATE);
创建类加载器 : 传入上述 4 4 4 个参数 , 创建类加载器 ;
// 创建 DexClassLoader
mDexClassLoader = new DexClassLoader(
loadPath, // 加载路径
optimizedDirectory.getAbsolutePath(), // apk 解压缓存目录
null,
context.getClassLoader() // DexClassLoader 加载器的父类加载器
);
注意 : 类加载时 , 只会加载一次 , 如果有重复的类 , 不会重复加载 ;
BootClassLoader 主要作用是加载 JDK 中的字节码类对象 ;
DexClassLoader 和 PathClassLoader 主要作用是加载 Android 和 引入的第三方库 中的字节码类对象 ;
三、加载资源
加载资源时需要使用到 AssetManager , 但是其构造函数是 隐藏 的 , 被 @Hide 注解 , 开发者无法直接调用 , 需要使用反射进行调用 ;
通过反射创建 AssetManager 对象 : 注意异常捕获 ;
// 加载资源
try {
// 通过反射创建 AssetManager
AssetManager assetManager = AssetManager.class.newInstance();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
创建 AssetManager 对象后 , 调用 addAssetPath 方法 , 添加资源的路径 , 用于加载插件包路径下的资源文件 ;
addAssetPath 也是隐藏方法 , 也是需要使用反射调用该方法 ;
// 通过反射获取 AssetManager 中的 addAssetPath 隐藏方法
Method addAssetPathMethod = assetManager.
getClass().
getDeclaredMethod("addAssetPath");
// 调用反射方法
addAssetPathMethod.invoke(assetManager, loadPath);
AssetManager 示例代码 :
public final class AssetManager implements AutoCloseable {
/**
* @hide
*/
@UnsupportedAppUsage
public AssetManager() {
}
/**
* @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)}
* @hide
*/
@Deprecated
@UnsupportedAppUsage
public int addAssetPath(String path) {
return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/);
}
}
获取 Resources 资源对象 : 通过上述加载插件资源后的 AssetManager 对象来创建 Resources 资源对象 ;
// 获取资源
mResources = new Resources(
assetManager,
context.getResources().getDisplayMetrics(),
context.getResources().getConfiguration()
);
传入的 DisplayMetrics metrics 和 Configuration config 参数从调用插件包的上下文中获取 ;
加载资源部分代码示例 :
首先 , 通过反射创建 AssetManager 对象 ;
然后 , 通过反射调用并执行 AssetManager 对象的 addAssetPath 方法 , 加载插件包资源 ;
最后 , 调用 Resources 构造函数 , 创建资源 , 传入 AssetManager 对象 和 上下文相关参数 ;
// 加载资源
try {
// 通过反射创建 AssetManager
AssetManager assetManager = AssetManager.class.newInstance();
// 通过反射获取 AssetManager 中的 addAssetPath 隐藏方法
Method addAssetPathMethod = assetManager.
getClass().
getDeclaredMethod("addAssetPath");
// 调用反射方法
addAssetPathMethod.invoke(assetManager, loadPath);
// 获取资源
mResources = new Resources(
assetManager,
context.getResources().getDisplayMetrics(),
context.getResources().getConfiguration()
);
} catch (IllegalAccessException e) {
// 调用 AssetManager.class.newInstance() 反射构造方法异常
e.printStackTrace();
} catch (InstantiationException e) {
// 调用 AssetManager.class.newInstance() 反射构造方法异常
e.printStackTrace();
} catch (NoSuchMethodException e) {
// getDeclaredMethod 反射方法异常
e.printStackTrace();
} catch (InvocationTargetException e) {
// invoke 执行反射方法异常
e.printStackTrace();
}
四、插件管理器完整代码
插件管理器完整代码 :
package com.example.plugin_core;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.res.AssetManager;
import android.content.res.Resources;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import dalvik.system.DexClassLoader;
/**
* 插件化框架核心类
*/
public class PluginManager {
/**
* 类加载器
* 用于加载插件包 apk 中的 classes.dex 文件中的字节码对象
*/
private DexClassLoader mDexClassLoader;
/**
* 从插件包 apk 中加载的资源
*/
private Resources mResources;
/**
* 插件包信息类
*/
private PackageInfo mPackageInfo;
/**
* 加载插件的上下文对象
*/
private Context mContext;
/**
* PluginManager 单例
*/
private static PluginManager instance;
private PluginManager(){
}
/**
* 获取单例类
* @return
*/
public static PluginManager getInstance(){
if (instance == null) {
instance = new PluginManager();
}
return instance;
}
/**
* 加载插件
* @param context 加载插件的应用的上下文
* @param loadPath 加载的插件包地址
*/
public void loadPlugin(Context context, String loadPath) {
this.mContext = context;
// DexClassLoader 的 optimizedDirectory 操作目录必须是私有的
// ( 模式必须是 Context.MODE_PRIVATE )
File optimizedDirectory = context.getDir("plugin", Context.MODE_PRIVATE);
// 创建 DexClassLoader
mDexClassLoader = new DexClassLoader(
loadPath, // 加载路径
optimizedDirectory.getAbsolutePath(), // apk 解压缓存目录
null,
context.getClassLoader() // DexClassLoader 加载器的父类加载器
);
// 加载资源
try {
// 通过反射创建 AssetManager
AssetManager assetManager = AssetManager.class.newInstance();
// 通过反射获取 AssetManager 中的 addAssetPath 隐藏方法
Method addAssetPathMethod = assetManager.
getClass().
getDeclaredMethod("addAssetPath");
// 调用反射方法
addAssetPathMethod.invoke(assetManager, loadPath);
// 获取资源
mResources = new Resources(
assetManager,
context.getResources().getDisplayMetrics(),
context.getResources().getConfiguration()
);
} catch (IllegalAccessException e) {
// 调用 AssetManager.class.newInstance() 反射构造方法异常
e.printStackTrace();
} catch (InstantiationException e) {
// 调用 AssetManager.class.newInstance() 反射构造方法异常
e.printStackTrace();
} catch (NoSuchMethodException e) {
// getDeclaredMethod 反射方法异常
e.printStackTrace();
} catch (InvocationTargetException e) {
// invoke 执行反射方法异常
e.printStackTrace();
}
}
}
五、博客资源
博客资源 :
- GitHub : https://github.com/han1202012/Plugin
以上是关于Android 插件化“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 )的主要内容,如果未能解决你的问题,请参考以下文章
Android 插件化“ 插桩式 “ 插件化框架 ( 获取插件入口 Activity 组件 | 加载插件 Resources 资源 )
Android 插件化“ 插桩式 “ 插件化框架 ( 代理 Activity 组件开发 )
Android 插件化“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 )
Android 插件化“ 插桩式 “ 插件化框架 ( 原理与实现思路 )