二、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(); }