【Android 插件化】Hook 插件化框架 ( 使用 Hook 方式替换插件 Activity 的 mResources 成员变量 )
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 的實例對象 ;
// 反射 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 實例對象 ;
// 反射獲取 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 成員變量 , 替換成開發者自己開發的代理類 ;
// 將 ActivityThread 對象中的 mInstrumentation 成員變量// 替換成自己的代理類reflector.set(new InstrumentationProxy(mInstrumentationObj));下面介紹 InstrumentationProxy 的實現 ;
上述代碼使用了 【Android 插件化】Hook 插件化框架 ( 反射工具類 | 反射常用操作整理 ) 中的反射工具類 ;
二、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 中的 ResourcesexchangeResourcesOfActivity(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*/@Overridepublic 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 中的 ResourcesexchangeResourcesOfActivity(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();}
四、完整代碼示例
1、HookUtils 完整代碼示例
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> IActivityManagerSingletonObject 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 代理類完整代碼示例
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 中的 ResourcesexchangeResourcesOfActivity(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*/@Overridepublic 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 中的 ResourcesexchangeResourcesOfActivity(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 中添加各種數據標識 , 根據標識位判定應該加載哪些資源 ;
在本示例中 , 只進行了是否加載插件包的標識位 , 用于區分 宿主應用組件 和 插件應用組件 ;
如果有多個插件包 , 可以將插件包名稱 , 序號作為標識位 , 為不同的插件包加載不同的插件資源 ;
// 這里注意 : 所有的 Activity 創建 , 都會過這個方法 , 這里只將插件包中的 Activity 的資源替換// 這里要做一個判斷// 不能修改宿主應用的資源// 只有插件包中的 Activity 才進行相應的修改// 在調用插件包中的組件時 , 在 Intent 中傳入一個 isPlugin 變量 ,// 也可以傳入插件的標志位 , 區分不同的插件包// 這里只有一個插件包 , 只設置一個 Boolean 變量即可if (!intent.getBooleanExtra("isPlugin", false)) return;六、博客資源
博客資源 :
- GitHub : https://github.com/han1202012/Plugin_Hook
總結
以上是生活随笔為你收集整理的【Android 插件化】Hook 插件化框架 ( 使用 Hook 方式替换插件 Activity 的 mResources 成员变量 )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【错误记录】Android Studio
- 下一篇: 【Android 插件化】Hook 插件