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

二、Instrumentation 代理类



1、持有被代理实例对象


在 Instrumentation 代理类中 , 持有被代理的对象 , 有一些操作需要使用原来的 Instrumentation 进行操作 , 在构造方法中注入被代理对象 ;


 

/**
     * 持有被代理对象
     * 有一些操作需要使用原来的 Instrumentation 进行操作
     */
    private final Instrumentation mBase;
   
    /**
     * 在构造方法中注入被代理对象
     * @param mBase
     */
    public InstrumentationProxy(Instrumentation mBase) {
        this.mBase = mBase;
    }



2、代理执行 execStartActivity 方法


代理执行 Instrumentation 方法 , 主要通过反射 android.app.Instrumentation 的 execStartActivity 方法 , 代理执行 真实的 Instrumentation 实例对象的 execStartActivity 方法 ;


 

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 对象 ;


   

/**
     * 在该方法中 , 可以拿到 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 变量即可 ;


     

// 这里注意 : 所有的 Activity 创建 , 都会过这个方法 , 这里只将插件包中的 Activity 的资源替换
        // 这里要做一个判断
        //      不能修改宿主应用的资源
        //      只有插件包中的 Activity 才进行相应的修改
        // 在调用插件包中的组件时 , 在 Intent 中传入一个 isPlugin 变量 ,
        //      也可以传入插件的标志位 , 区分不同的插件包
        //      这里只有一个插件包 , 只设置一个 Boolean 变量即可
        if (!intent.getBooleanExtra("isPlugin", false)) return;


启动插件包 Activity 示例 :


// 启动插件包中的 Activity
Intent pluginIntent = new Intent();
pluginIntent.setComponent(new ComponentName("com.example.plugin",
        "com.example.plugin.MainActivity"));
pluginIntent.putExtra("isPlugin", true);
startActivity(pluginIntent);



2、反射 ContextThemeWrapper 类


       

// 反射 ContextThemeWrapper 类 , Activity 是 ContextThemeWrapper 的子类
        // Resources mResources 成员定义在 ContextThemeWrapper 中
        Class<?> contextThemeWrapperClass = null;
        try {
            contextThemeWrapperClass = Class.forName("android.view.ContextThemeWrapper");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }


3、反射获取 ContextThemeWrapper 类的 mResources 字段


     

// 反射获取 ContextThemeWrapper 类的 mResources 字段
        Field mResourcesField = null;
        try {
            mResourcesField = contextThemeWrapperClass.getDeclaredField("mResources");
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        // 设置字段可见性
        mResourcesField.setAccessible(true);


4、将插件资源设置到插件 Activity 中


     

// 将插件资源设置到插件 Activity 中
        try {
            mResourcesField.set(activity, PluginManager.getInstance(activity).getResources());
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }



上一篇:小刚带你深入浅出理解Lua语言


下一篇:beanutils 入门