【Android 插件化】Hook 插件化框架 ( 使用 Hook 方式替换插件 Activity 的 mResources 成员变量 )

2023-03-29 15:26:13 浏览数 (1)

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

0 人点赞