【Android 插件化】“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 )
Android 插件化系列文章目录
【Android 插件化】插件化简介 ( 组件化与插件化 )
【Android 插件化】插件化原理 ( JVM 内存数据 | 类加载流程 )
【Android 插件化】插件化原理 ( 类加载器 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 原理与实现思路 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 注入上下文的使用 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 获取插件入口 Activity 组件 | 加载插件 Resources 资源 )
【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!");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
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);
- 1
- 2
- 3
创建类加载器 : 传入上述 4 4 4 个参数 , 创建类加载器 ;
// 创建 DexClassLoader
mDexClassLoader = new DexClassLoader(
loadPath, // 加载路径
optimizedDirectory.getAbsolutePath(), // apk 解压缓存目录
null,
context.getClassLoader() // DexClassLoader 加载器的父类加载器
);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
注意 : 类加载时 , 只会加载一次 , 如果有重复的类 , 不会重复加载 ;
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();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
创建 AssetManager 对象后 , 调用 addAssetPath 方法 , 添加资源的路径 , 用于加载插件包路径下的资源文件 ;
addAssetPath 也是隐藏方法 , 也是需要使用反射调用该方法 ;
// 通过反射获取 AssetManager 中的 addAssetPath 隐藏方法
Method addAssetPathMethod = assetManager.
getClass().
getDeclaredMethod("addAssetPath");
// 调用反射方法
addAssetPathMethod.invoke(assetManager, loadPath);
- 1
- 2
- 3
- 4
- 5
- 6
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*/);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
获取 Resources 资源对象 : 通过上述加载插件资源后的 AssetManager 对象来创建 Resources 资源对象 ;
// 获取资源
mResources = new Resources(
assetManager,
context.getResources().getDisplayMetrics(),
context.getResources().getConfiguration()
);
- 1
- 2
- 3
- 4
- 5
- 6
传入的 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();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
四、插件管理器完整代码
插件管理器完整代码 :
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();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
五、博客资源
博客资源 :
- GitHub : https://github.com/han1202012/Plugin
文章来源: hanshuliang.blog.csdn.net,作者:韩曙亮,版权归原作者所有,如需转载,请联系作者。
原文链接:hanshuliang.blog.csdn.net/article/details/117453759
- 点赞
- 收藏
- 关注作者
评论(0)