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 插件化】Hook 插件化框架 ( 从源码角度分析加载资源流程 | Hook 点选择 | 资源冲突解决方案 )
【Android 插件化】Hook 插件化框架 ( 使用 Hook 方式替换插件 Activity 的 mResources 成员变量 )
文章目录
- Android 插件化系列文章目录
- 前言
- 一、使用 Hook 方式替换插件 Activity 资源
-
- 1、通过反射获取 ActivityThread 及 实例对象
- 2、通过反射获取 Instrumentation 实例对象
- 3、通过反射替换 ActivityThread 中的 mInstrumentation 成员
- 二、Instrumentation 代理类
-
- 1、持有被代理实例对象
- 2、代理执行 execStartActivity 方法
- 3、截获 Activity 实例对象
- 4、替换 Activity 中的 mResources 成员
- 三、替换 Activity 中的 mResources 成员
-
- 1、判断 Activity 是否是插件中的组件
- 2、反射 ContextThemeWrapper 类
- 3、反射获取 ContextThemeWrapper 类的 mResources 字段
- 4、将插件资源设置到插件 Activity 中
- 四、完整代码示例
-
- 1、HookUtils 完整代码示例
- 2、Instrumentation 代理类完整代码示例
- 五、为不同的插件包设置不同的资源
- 六、博客资源
前言
在上一篇博客 【Android 插件化】Hook 插件化框架 ( 从源码角度分析加载资源流程 | Hook 点选择 | 资源冲突解决方案 ) 中 , 分析了加载插件资源 , 并替换 Activity 中 Resources 成员可用的 Hook 点 , 本篇博客开始实现插件资源的加载与替换 ;
一、使用 Hook 方式替换插件 Activity 资源
1、通过反射获取 ActivityThread 及 实例对象
首先反射 android.app.ActivityThread 类 , 该类是单例类 , 其唯一的实例对象定义在 sCurrentActivityThread 静态成员中 , 通过反射 , 可以获取 ActivityThread 的实例对象 ;
代码语言:javascript复制 // 反射 ActivityThread 类
// 反射获取 ActivityThread 类中的 sCurrentActivityThread 静态成员
// 这是单例类内部的静态成员
Object sCurrentActivityThreadObj =
Reflector.on("android.app.ActivityThread") // 反射 ActivityThread 类
.field("sCurrentActivityThread") // 获取 sCurrentActivityThread 字段
.get(); // 获取 sCurrentActivityThread 对象
上述代码使用了 【Android 插件化】Hook 插件化框架 ( 反射工具类 | 反射常用操作整理 ) 中的反射工具类 ;
2、通过反射获取 Instrumentation 实例对象
在 ActivityThread 中有 mInstrumentation 成员变量 , 这个就是 Android 启动过程中的 Instrumentation ;
最终目的是替换 sCurrentActivityThread 中的 mInstrumentation 字段 , 使用我们自定义的 Instrumentation 代理类 , 替换实际的 Instrumentation 实例对象 ;
代码语言:javascript复制 // 反射获取 ActivityThread 对象中的 mInstrumentation 成员变量
// 目的是替换 sCurrentActivityThread 中的 mInstrumentation 字段
Reflector reflector =
Reflector.on("android.app.ActivityThread") // 反射 ActivityThread 类
.field("mInstrumentation") // 获取 mInstrumentation 字段
.with(sCurrentActivityThreadObj); // 设置 ActivityThread 实例对象
// 获取 ActivityThread 中的 mInstrumentationObj 成员, 创建 Instrumentation 静态代理时使用
Instrumentation mInstrumentationObj = (Instrumentation) reflector.get();
上述代码使用了 【Android 插件化】Hook 插件化框架 ( 反射工具类 | 反射常用操作整理 ) 中的反射工具类 ;
3、通过反射替换 ActivityThread 中的 mInstrumentation 成员
将 ActivityThread 对象中的 mInstrumentation 成员变量 , 替换成开发者自己开发的代理类 ;
代码语言:javascript复制 // 将 ActivityThread 对象中的 mInstrumentation 成员变量
// 替换成自己的代理类
reflector.set(new InstrumentationProxy(mInstrumentationObj));
下面介绍 InstrumentationProxy 的实现 ;
上述代码使用了 【Android 插件化】Hook 插件化框架 ( 反射工具类 | 反射常用操作整理 ) 中的反射工具类 ;
二、Instrumentation 代理类
1、持有被代理实例对象
在 Instrumentation 代理类中 , 持有被代理的对象 , 有一些操作需要使用原来的 Instrumentation 进行操作 , 在构造方法中注入被代理对象 ;
代码语言:javascript复制 /**
* 持有被代理对象
* 有一些操作需要使用原来的 Instrumentation 进行操作
*/
private final Instrumentation mBase;
/**
* 在构造方法中注入被代理对象
* @param mBase
*/
public InstrumentationProxy(Instrumentation mBase) {
this.mBase = mBase;
}
2、代理执行 execStartActivity 方法
代理执行 Instrumentation 方法 , 主要通过反射 android.app.Instrumentation 的 execStartActivity 方法 , 代理执行 真实的 Instrumentation 实例对象的 execStartActivity 方法 ;
代码语言:javascript复制 public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
ActivityResult result = null;
// 反射调用 Instrumentation mBase 成员的 execStartActivity 方法
result = Reflector.on("android.app.Instrumentation")
.method("execStartActivity", // 反射的方法名
Context.class, // 后续都是方法的参数类型
IBinder.class,
IBinder.class,
Activity.class,
Intent.class,
int.class,
Bundle.class)
.with(mBase)
.call(who, // 后续都是传入 execStartActivity 方法的参数
contextThread,
token,
target,
intent,
requestCode,
options);
return result;
}
3、截获 Activity 实例对象
newActivity 方法是创建 Activity 实例对象的方法 , 在该方法中可以获取到创建的 Activity 对象 ;
代码语言:javascript复制 /**
* 在该方法中 , 可以拿到 Activity , 通过反射修改 Activity 中的 Resources 成员变量
* @param cl
* @param className
* @param intent
* @return
* @throws ClassNotFoundException
* @throws IllegalAccessException
* @throws InstantiationException
*/
public Activity newActivity(ClassLoader cl, String className, Intent intent) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Activity activity = mBase.newActivity(cl, className, intent);
// 替换 Activity 中的 Resources
exchangeResourcesOfActivity(activity, intent);
return activity;
}
/**
* 在该方法中 , 可以拿到 Activity , 通过反射修改 Activity 中的 Resources 成员变量
* @param clazz
* @param context
* @param token
* @param application
* @param intent
* @param info
* @param title
* @param parent
* @param id
* @param lastNonConfigurationInstance
* @return
* @throws IllegalAccessException
* @throws InstantiationException
*/
@Override
public Activity newActivity(Class<?> clazz, Context context, IBinder token, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, Object lastNonConfigurationInstance) throws IllegalAccessException, InstantiationException {
Activity activity = mBase.newActivity(clazz, context, token, application, intent, info, title, parent, id, lastNonConfigurationInstance);
// 替换 Activity 中的 Resources
exchangeResourcesOfActivity(activity, intent);
return activity;
}
4、替换 Activity 中的 mResources 成员
这个步骤比较重要 , 在下面介绍 ;
三、替换 Activity 中的 mResources 成员
1、判断 Activity 是否是插件中的组件
有的 Activity 创建 , 都会过这个方法 , 这里只将插件包中的 Activity 的资源替换 ;
这里要做一个判断 , 不能修改宿主应用的资源 , 只有插件包中的 Activity 才进行相应的修改 ;
在启动插件包中的组件时 , 在 Intent 中传入一个 isPlugin 变量 , 也可以传入插件的标志位 , 区分不同的插件包 , 这里只有一个插件包 , 只设置一个 Boolean 变量即可 ;
代码语言:javascript复制 // 这里注意 : 所有的 Activity 创建 , 都会过这个方法 , 这里只将插件包中的 Activity 的资源替换
// 这里要做一个判断
// 不能修改宿主应用的资源
// 只有插件包中的 Activity 才进行相应的修改
// 在调用插件包中的组件时 , 在 Intent 中传入一个 isPlugin 变量 ,
// 也可以传入插件的标志位 , 区分不同的插件包
// 这里只有一个插件包 , 只设置一个 Boolean 变量即可
if (!intent.getBooleanExtra("isPlugin", false)) return;
启动插件包 Activity 示例 :
代码语言:javascript复制// 启动插件包中的 Activity
Intent pluginIntent = new Intent();
pluginIntent.setComponent(new ComponentName("com.example.plugin",
"com.example.plugin.MainActivity"));
pluginIntent.putExtra("isPlugin", true);
startActivity(pluginIntent);
2、反射 ContextThemeWrapper 类
代码语言:javascript复制 // 反射 ContextThemeWrapper 类 , Activity 是 ContextThemeWrapper 的子类
// Resources mResources 成员定义在 ContextThemeWrapper 中
Class<?> contextThemeWrapperClass = null;
try {
contextThemeWrapperClass = Class.forName("android.view.ContextThemeWrapper");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
3、反射获取 ContextThemeWrapper 类的 mResources 字段
代码语言:javascript复制 // 反射获取 ContextThemeWrapper 类的 mResources 字段
Field mResourcesField = null;
try {
mResourcesField = contextThemeWrapperClass.getDeclaredField("mResources");
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
// 设置字段可见性
mResourcesField.setAccessible(true);
4、将插件资源设置到插件 Activity 中
代码语言:javascript复制 // 将插件资源设置到插件 Activity 中
try {
mResourcesField.set(activity, PluginManager.getInstance(activity).getResources());
} catch (IllegalAccessException e) {
e.printStackTrace();
}
四、完整代码示例
1、HookUtils 完整代码示例
代码语言:javascript复制package kim.hsl.plugin;
import android.app.Instrumentation;
import android.content.Context;
import android.os.Handler;
import android.util.Log;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
/**
* 主要职责 : Hook Activity 的启动过程
* 本工具类只针对 API Level 28 实现 , 如果是完整插件化框架 , 需要实现所有版本的 Hook 过程
* 不同的版本 , Activity 的启动过程是不同的 , 需要逐个根据 Activity 启动源码进行 Hook 适配
*/
public class HookUtils {
private static final String TAG = "HookUtils";
/**
* 最终目的是劫持 ActivityManagerService 的 startActivity 方法 ,
* 修改 Intent 中药启动的 Activity 类
*/
public static void hookAms(Context context){
// 获取 android.app.ActivityManager 类
Class<?> activityManagerClass = null;
try {
activityManagerClass = Class.forName("android.app.ActivityManager");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 获取 android.app.ActivityManager 类 中的 IActivityManagerSingleton 属性
// private static final Singleton<IActivityManager> IActivityManagerSingleton 成员变量
Field iActivityManagerSingletonField = null;
try {
iActivityManagerSingletonField =
activityManagerClass.getDeclaredField("IActivityManagerSingleton");
// 设置成员字段的可访问性
iActivityManagerSingletonField.setAccessible(true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
// 获取 android.app.ActivityManager 类的静态成员变量
// private static final Singleton<IActivityManager> IActivityManagerSingleton
// 直接调用 Field 字段 iActivityManagerSingletonField 的 get 方法 , 传入 null 即可获取
Object iActivityManagerSingletonObject = null;
try {
iActivityManagerSingletonObject = iActivityManagerSingletonField.get(null);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// 获取 Singleton 类
// ActivityManager 中的 IActivityManagerSingleton 成员是 Singleton<IActivityManager> 类型的
Class<?> singletonClass = null;
try {
singletonClass = Class.forName("android.util.Singleton");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 反射获取 Singleton 类中的 mInstance 字段
Field mInstanceField = null;
try {
mInstanceField = singletonClass.getDeclaredField("mInstance");
// 设置字段的可访问性
mInstanceField.setAccessible(true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
// 反射获取 Singleton 类中的 mInstance 成员对象
// 该 mInstanceObject 成员对象就是 IActivityManager
// private static final Singleton<IActivityManager> IActivityManagerSingleton
Object mInstanceObject = null;
try {
mInstanceObject = mInstanceField.get(iActivityManagerSingletonObject);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// 使用动态代理 , 替换 android.app.ActivityManager 中的
// private static final Singleton<IActivityManager> IActivityManagerSingleton 成员的
// mInstance 成员
// 注意 : 该操作一定要在 AMS 启动之前将原来的 Intent 替换掉
// 之后还要替换回去
// 使用 Intent 启动插件包时 , 一般都使用隐式启动
// 调用 Intent 的 setComponent , 通过包名和类名创建 Component ,
// 这样操作 , 即使没有获得 Activity 引用 , 也不会报错
// 该插件包中的 Activity 没有在 "宿主" 应用中注册 , 因此启动报错
// AMS 会干掉没有注册过的 Activity
// 这里先在启动 AMS 之前 , 设置一个已经 注册过的 占坑 Activity ( StubActivity ) 执行启动流程
// 在主线程生成 Activity 实例对象时 , 还需要恢复插件包中的 Activity
// IActivityManager 是接口
// 这是一个 AIDL 文件生成的 , 由 IActivityManager.aidl 生成
Class<?> IActivityManagerInterface = null;
try {
IActivityManagerInterface = Class.forName("android.app.IActivityManager");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 动态代理的实际代理类
AmsInvocationHandler amsInvocationHandler =
new AmsInvocationHandler(context, mInstanceObject);
// 动态代理过程
Object proxy = Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(), // 类加载器
new Class[]{IActivityManagerInterface}, // 接口
amsInvocationHandler); // 代理的对象
// 使用动态代理类 , 替换原来的 ActivityManager 中的 IActivityManagerSingleton 成员
// 的 Singleton 类中的 mInstance 成员
try {
mInstanceField.set(iActivityManagerSingletonObject, proxy);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
/**
* 劫持 Activity Thread 的 final H mH = new H(); 成员
* 该成员类型是 class H extends Handler ;
* @param context
*/
public static void hookActivityThread(Context context) {
// 反射获取 ActivityThread 类
Class<?> activityThreadClass = null;
try {
activityThreadClass = Class.forName("android.app.ActivityThread");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// Activity Thread 是一个单例 , 内部的单例成员是
// private static volatile ActivityThread sCurrentActivityThread;
// 可以直接通过 ActivityThread 类 , 获取该单例对象
// 这也是 Hook 点优先找静态变量的原因 , 静态变量对象容易拿到 , 通过反射即可获取 , 不涉及系统源码相关操作
Field sCurrentActivityThreadField = null;
try {
sCurrentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
// 反射获取的字段一般都要设置可见性
sCurrentActivityThreadField.setAccessible(true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
// 获取类的静态变量 , 使用 字段.get(null) 即可
Object activityThreadObject = null;
try {
activityThreadObject = sCurrentActivityThreadField.get(null);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// 获取 Activity Thread 中的 final H mH = new H() 成员字段 ;
Field mHField = null;
try {
mHField = activityThreadClass.getDeclaredField("mH");
// 设置该字段的可见性
mHField.setAccessible(true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
// 通过反射获取 Activity Thread 中的 final H mH = new H() 成员实例对象
Handler mHObject = null;
try {
mHObject = (Handler) mHField.get(activityThreadObject);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
Class<?> handlerClass = null;
try {
handlerClass = Class.forName("android.os.Handler");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 通过反射获取 final H mH = new H() 成员的 mCallback 成员字段
// Handler 中有成员变量 final Callback mCallback;
Field mCallbackField = null;
try {
// 类可以直接获取到, 可以不用反射
mCallbackField = Handler.class.getDeclaredField("mCallback");
//mCallbackField = mHObject.getClass().getDeclaredField("mCallback");
// 设置字段的可见性
mCallbackField.setAccessible(true);
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
// 使用静态代理类 HandlerProxy , 替换 final H mH = new H() 成员实例对象中的 mCallback 成员
HandlerProxy proxy = new HandlerProxy();
try {
Log.i(TAG, "mCallbackField : " mCallbackField " , mHObject : " mHObject " , proxy : " proxy);
mCallbackField.set(mHObject, proxy);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 主要用于 Resources 资源的加载
*/
public static void hookInstrumentation() {
// 反射 ActivityThread 类
// 反射获取 ActivityThread 类中的 sCurrentActivityThread 静态成员
// 这是单例类内部的静态成员
Object sCurrentActivityThreadObj =
Reflector.on("android.app.ActivityThread") // 反射 ActivityThread 类
.field("sCurrentActivityThread") // 获取 sCurrentActivityThread 字段
.get(); // 获取 sCurrentActivityThread 对象
// 反射获取 ActivityThread 对象中的 mInstrumentation 成员变量
// 目的是替换 sCurrentActivityThread 中的 mInstrumentation 字段
Reflector reflector =
Reflector.on("android.app.ActivityThread") // 反射 ActivityThread 类
.field("mInstrumentation") // 获取 mInstrumentation 字段
.with(sCurrentActivityThreadObj); // 设置 ActivityThread 实例对象
// 获取 ActivityThread 中的 mInstrumentationObj 成员, 创建 Instrumentation 静态代理时使用
Instrumentation mInstrumentationObj = (Instrumentation) reflector.get();
// 将 ActivityThread 对象中的 mInstrumentation 成员变量
// 替换成自己的代理类
reflector.set(new InstrumentationProxy(mInstrumentationObj));
}
}
2、Instrumentation 代理类完整代码示例
代码语言:javascript复制package kim.hsl.plugin;
import android.app.Activity;
import android.app.Application;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.IBinder;
import java.lang.reflect.Field;
public class InstrumentationProxy extends Instrumentation {
private static final String TAG = "InstrumentationProxy";
/**
* 持有被代理对象
* 有一些操作需要使用原来的 Instrumentation 进行操作
*/
private final Instrumentation mBase;
/**
* 在构造方法中注入被代理对象
* @param mBase
*/
public InstrumentationProxy(Instrumentation mBase) {
this.mBase = mBase;
}
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
ActivityResult result = null;
// 反射调用 Instrumentation mBase 成员的 execStartActivity 方法
result = Reflector.on("android.app.Instrumentation")
.method("execStartActivity", // 反射的方法名
Context.class, // 后续都是方法的参数类型
IBinder.class,
IBinder.class,
Activity.class,
Intent.class,
int.class,
Bundle.class)
.with(mBase)
.call(who, // 后续都是传入 execStartActivity 方法的参数
contextThread,
token,
target,
intent,
requestCode,
options);
return result;
}
/**
* 在该方法中 , 可以拿到 Activity , 通过反射修改 Activity 中的 Resources 成员变量
* @param cl
* @param className
* @param intent
* @return
* @throws ClassNotFoundException
* @throws IllegalAccessException
* @throws InstantiationException
*/
public Activity newActivity(ClassLoader cl, String className, Intent intent) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Activity activity = mBase.newActivity(cl, className, intent);
// 替换 Activity 中的 Resources
exchangeResourcesOfActivity(activity, intent);
return activity;
}
/**
* 在该方法中 , 可以拿到 Activity , 通过反射修改 Activity 中的 Resources 成员变量
* @param clazz
* @param context
* @param token
* @param application
* @param intent
* @param info
* @param title
* @param parent
* @param id
* @param lastNonConfigurationInstance
* @return
* @throws IllegalAccessException
* @throws InstantiationException
*/
@Override
public Activity newActivity(Class<?> clazz, Context context, IBinder token, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, Object lastNonConfigurationInstance) throws IllegalAccessException, InstantiationException {
Activity activity = mBase.newActivity(clazz, context, token, application, intent, info, title, parent, id, lastNonConfigurationInstance);
// 替换 Activity 中的 Resources
exchangeResourcesOfActivity(activity, intent);
return activity;
}
/**
* 反射 Activity , 并设置 Activity 中 Resources 成员变量
* @param activity
* @param intent
*/
private void exchangeResourcesOfActivity(Activity activity, Intent intent) {
// 这里注意 : 所有的 Activity 创建 , 都会过这个方法 , 这里只将插件包中的 Activity 的资源替换
// 这里要做一个判断
// 不能修改宿主应用的资源
// 只有插件包中的 Activity 才进行相应的修改
// 在调用插件包中的组件时 , 在 Intent 中传入一个 isPlugin 变量 ,
// 也可以传入插件的标志位 , 区分不同的插件包
// 这里只有一个插件包 , 只设置一个 Boolean 变量即可
if (!intent.getBooleanExtra("isPlugin", false)) return;
try {
// 获取插件资源
Resources pluginResources = PluginManager.getInstance(activity).getResources();
// 反射 ContextThemeWrapper 类 , Activity 是 ContextThemeWrapper 的子类
// Resources mResources 成员定义在 ContextThemeWrapper 中
Class<?> contextThemeWrapperClass = Class.forName("android.view.ContextThemeWrapper");
// 反射获取 AppCompatActivity 类的 mResources 字段
Field mResourcesField = contextThemeWrapperClass.getDeclaredField("mResources");
// 设置字段可见性
mResourcesField.setAccessible(true);
// 将插件资源设置为
mResourcesField.set(activity, PluginManager.getInstance(activity).getResources());
} catch (Exception e) {
e.printStackTrace();
}
}
}
五、为不同的插件包设置不同的资源
在 Hook Instrumentation 的时候 , 替换 Activity 的 mResources 成员前 , 要先进行判定 ;
启动插件包组件时 , 可以向 Intent 中添加各种数据标识 , 根据标识位判定应该加载哪些资源 ;
在本示例中 , 只进行了是否加载插件包的标识位 , 用于区分 宿主应用组件 和 插件应用组件 ;
如果有多个插件包 , 可以将插件包名称 , 序号作为标识位 , 为不同的插件包加载不同的插件资源 ;
代码语言:javascript复制 // 这里注意 : 所有的 Activity 创建 , 都会过这个方法 , 这里只将插件包中的 Activity 的资源替换
// 这里要做一个判断
// 不能修改宿主应用的资源
// 只有插件包中的 Activity 才进行相应的修改
// 在调用插件包中的组件时 , 在 Intent 中传入一个 isPlugin 变量 ,
// 也可以传入插件的标志位 , 区分不同的插件包
// 这里只有一个插件包 , 只设置一个 Boolean 变量即可
if (!intent.getBooleanExtra("isPlugin", false)) return;
六、博客资源
博客资源 :
- GitHub : https://github.com/han1202012/Plugin_Hook