Android 插件化系列文章目录
【Android 插件化】插件化简介 ( 组件化与插件化 )
【Android 插件化】插件化原理 ( JVM 内存数据 | 类加载流程 )
【Android 插件化】插件化原理 ( 类加载器 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 原理与实现思路 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 注入上下文的使用 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 获取插件入口 Activity 组件 | 加载插件 Resources 资源 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 运行应用 | 代码整理 )
【Android 插件化】Hook 插件化框架 ( Hook 技术 | 代理模式 | 静态代理 | 动态代理 )
【Android 插件化】Hook 插件化框架 ( Hook 实现思路 | Hook 按钮点击事件 )
【Android 插件化】Hook 插件化框架 ( Hook Activity 启动过程 | 静态代理 )
【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 一 | Activity 进程相关源码 )
【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 二 | AMS 进程相关源码 | 主进程相关源码 )
【Android 插件化】Hook 插件化框架 ( hook 插件化原理 | 插件包管理 )
【Android 插件化】Hook 插件化框架 ( 通过反射获取 “插件包“ 中的 Element[] dexElements )
【Android 插件化】Hook 插件化框架 ( 通过反射获取 “宿主“ 应用中的 Element[] dexElements )
【Android 插件化】Hook 插件化框架 ( 合并 “插件包“ 与 “宿主“ 中的 Element[] dexElements | 设置合并后的 Element[] 数组 )
【Android 插件化】Hook 插件化框架 ( 创建插件应用 | 拷贝插件 APK | 初始化插件包 | 测试插件 DEX 字节码 )
【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | Hook 点分析 )
【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | 反射获取 IActivityManager 对象 )
【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | AMS 启动前使用动态代理替换掉插件 Activity 类 )
【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | 主线程创建 Activity 实例之前使用插件 Activity 类替换占位的组件 )
【Android 插件化】Hook 插件化框架 ( 反射工具类 | 反射常用操作整理 )
【Android 插件化】Hook 插件化框架 ( 插件包资源加载 )
文章目录
- Android 插件化系列文章目录
- 前言
- 一、加载插件包资源文件
-
- 1、反射 AssetManager 类并创建实例对象
- 2、反射 AssetManager 的 addAssetPath 方法并调用
- 3、创建 Resources
- 二、自定义 Application 中设置插件资源
- 三、插件 Activity 组件中设置插件资源
- 四、博客资源
前言
在
【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | AMS 启动前使用动态代理替换掉插件 Activity 类 )
【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | 主线程创建 Activity 实例之前使用插件 Activity 类替换占位的组件 )
博客中 , 基本已经完成了插件 Activity 的实例化操作 , 已经可以启动插件 Activity , 只剩下资源文件没有获取 ;
本博客开始加载插件包中的资源文件 ;
一、加载插件包资源文件
在插件包中的 Activity , 如果加载 R.layout.activity_main , 拿到的是 “宿主” 应用中的资源 , 无法拿到插件包中的资源 ;
1、反射 AssetManager 类并创建实例对象
反射 AssetManager 类 , 并创建 AssetManager 实例对象 ;
代码语言:javascript复制 // 使用反射工具类进行链式调用 , 创建 AssetManager 对象
AssetManager assetManager = Reflector.on(AssetManager.class).newInstance();
涉及到的反射工具类内容 : 上述代码使用了上一篇博客 【Android 插件化】Hook 插件化框架 ( 反射工具类 | 反射常用操作整理 ) 中的 反射工具类 , 涉及到的反射代码如下 :
代码语言:javascript复制/**
* 封装反射相关逻辑的工具类
* 该封装类会维持链式调用
*/
public class Reflector {
/**
* 反射某个类的入口方法
*
* @param type 要反射的类
* @return
*/
public static Reflector on(Class<?> type) {
Reflector reflector = new Reflector();
reflector.mClass = type;
return reflector;
}
/**
* 创建 mClass 类型的实例对象
* @param <T>
* @return
* @throws Exception
*/
public <T> T newInstance() {
try {
return (T) mClass.newInstance();
} catch (IllegalAccessException e) {
e.printStackTrace();
return null;
} catch (InstantiationException e) {
e.printStackTrace();
return null;
}
}
}
2、反射 AssetManager 的 addAssetPath 方法并调用
通过反射获取 AssetManager 的 addAssetPath 方法 , 方法的参数是 String 类型 ;
执行该方法 , 传入当前插件包 APK 的路径 ;
代码语言:javascript复制 // 获取插件包 APK 文件路径 , 加载该 APK 下的资源
// /data/user/0/com.example.plugin_hook/files/plugin.apk
String pluginPath = mBase.getFilesDir() "/plugin.apk";
// 使用反射调用 AssetManager 中的 addAssetPath 方法 , 传入 APK 插件包的路径
// addAssetPath 方法的参数为 /data/user/0/com.example.plugin_hook/files/plugin.apk
Reflector.on(assetManager).method("addAssetPath", String.class).call(pluginPath);
涉及到的反射工具类内容 : 上述代码使用了上一篇博客 【Android 插件化】Hook 插件化框架 ( 反射工具类 | 反射常用操作整理 ) 中的 反射工具类 , 涉及到的反射代码如下 :
代码语言:javascript复制/**
* 封装反射相关逻辑的工具类
* 该封装类会维持链式调用
*/
public class Reflector {
/**
* 反射某个类的入口方法
*
* @param type 要反射的类
* @return
*/
public static Reflector on(Class<?> type) {
Reflector reflector = new Reflector();
reflector.mClass = type;
return reflector;
}
/**
* 反射某个类的入口方法
*
* @param className 要反射的类名
* @return
*/
public static Reflector on(String className) {
try {
return on(Class.forName(className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
/**
* 反射某个类的入口方法
*
* @param object 反射类对应的实例对象
* @return
*/
public static Reflector on(Object object) {
return on(object.getClass()).with(object);
}
/**
* 反射类中的某个方法
*
* @param name
* @param args
* @return
*/
public Reflector method(String name, Class<?>... args) {
mMethod = findMethod(name, args);
mMethod.setAccessible(true);
return this;
}
/**
* 根据方法名 和 参数名称 , 查找 Method 方法
* 首先在本类中查找
* 如果找到直接返回字段
* 如果在本类中没有找到 , 就去遍历它的父类 , 尝试在父类中查找该字段
* 如果有父类 , 则在父类中查找
* 如果在父类中找到 , 返回该字段
* 如果在父类中没有找到 , 则返回空
* 如果没有父类 , 返回空
*
* 尽量传具体的正确的类 , 不要传子类
* @param name
* @param args
* @return
*/
private Method findMethod(String name, Class<?>... args) {
try {
// 首先在本类中查找 , 如果找到直接返回方法
return mClass.getDeclaredMethod(name, args);
} catch (NoSuchMethodException e) {
// 如果在本类中没有找到 , 就去遍历它的父类 , 尝试在父类中查找该方法
for (Class<?> cls = mClass; cls != null; cls = cls.getSuperclass()) {
try {
// 如果在父类中找到 , 返回该字段
return cls.getDeclaredMethod(name);
} catch (NoSuchMethodException ex) {
// 如果在父类中没有找到 , 则返回空
return null;
}
}
// 如果没有父类, 则返回空
return null;
}
}
/**
* 调用 mCaller 的 mMethod 方法
*
* @param args
* @param <T>
* @return
*/
public <T> T call(Object... args) {
try {
return (T) mMethod.invoke(mCaller, args);
} catch (IllegalAccessException e) {
e.printStackTrace();
return null;
} catch (InvocationTargetException e) {
e.printStackTrace();
return null;
}
}
}
3、创建 Resources
创建 Resources , 传入 AssetManager 对象作为主要参数 , 其它参数都可以从应用的 Resources 中获取 ;
代码语言:javascript复制 // 创建 Resources 并返回
return mResources = new Resources(
assetManager,
mBase.getResources().getDisplayMetrics(),
mBase.getResources().getConfiguration()
二、自定义 Application 中设置插件资源
加载到插件资源后 , 重写 Application 的 public Resources getResources() 方法 , 返回插件包方法 ;
代码语言:javascript复制 @Override
public Resources getResources() {
if (pluginResources != null)
return pluginResources;
return super.getResources();
}
完整代码示例 :
代码语言:javascript复制package com.example.plugin_hook;
import android.app.Application;
import android.content.res.Resources;
import android.util.Log;
import java.io.File;
import kim.hsl.plugin.PluginManager;
public class MyApplication extends Application {
private static final String TAG = "plugin_MyApplication";
/**
* 插件资源
*/
private Resources pluginResources;
@Override
public void onCreate() {
super.onCreate();
// 如果已经存在文件, 先删除 , 防止拷贝过程中出错
File pluginFile = new File(getFilesDir() "/plugin.apk");
if (pluginFile.exists()){
pluginFile.delete();
}
// 先将 assets 中的插件包拷贝到 内置存储中
CommandUtils.copyAssets2File(
this,
"plugin.apk",
getFilesDir() "/plugin.apk");
// 将文件从 assets/plugin.apk 拷贝到 /data/user/0/com.example.plugin_hook/files/plugin.apk
Log.i(TAG, "将文件从 assets/plugin.apk 拷贝到 " getFilesDir() "/plugin.apk");
// 初始化插件包
PluginManager.getInstance(this).init();
Log.i(TAG, "插件化 初始化完毕");
// 设置插件包中的资源文件
pluginResources = PluginManager.getInstance(this).getmResources();
}
@Override
public Resources getResources() {
if (pluginResources != null)
return pluginResources;
return super.getResources();
}
}
三、插件 Activity 组件中设置插件资源
在 Activity 中重写 public Resources getResources() 方法 , 核心是返回插件资源 ;
代码语言:javascript复制 @Override
public Resources getResources() {
if (getApplication() != null && getApplication().getResources() != null) {
return getApplication().getResources();
}
return super.getResources();
}
完整代码示例 :
代码语言:javascript复制package com.example.plugin;
import androidx.appcompat.app.AppCompatActivity;
import android.app.Activity;
import android.content.res.Resources;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends Activity {
private static final String TAG = "plugin_MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i("plugin", "启动了插件 Activity");
}
public void log(){
Log.i(TAG, "Plugin MainActivity");
}
@Override
public Resources getResources() {
if (getApplication() != null && getApplication().getResources() != null) {
return getApplication().getResources();
}
return super.getResources();
}
}
四、博客资源
博客资源 :
- GitHub : https://github.com/han1202012/Plugin_Hook