【Android 插件化】Hook 插件化框架 ( 加载插件包资源 )
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 插件化系列文章目錄
- 前言
- 一、加載插件包資源文件
- 1、反射 AssetManager 類并創建實例對象
- 2、反射 AssetManager 的 addAssetPath 方法并調用
- 3、創建 Resources
- 二、自定義 Application 中設置插件資源
- 三、插件 Activity 組件中設置插件資源
- 四、博客資源
前言
在
【Android 插件化】Hook 插件化框架 ( Hook Activity 啟動流程 | AMS 啟動前使用動態代理替換掉插件 Activity 類 )
【Android 插件化】Hook 插件化框架 ( Hook Activity 啟動流程 | 主線程創建 Activity 實例之前使用插件 Activity 類替換占位的組件 )
博客中 , 基本已經完成了插件 Activity 的實例化操作 , 已經可以啟動插件 Activity , 只剩下資源文件沒有獲取 ;
本博客開始加載插件包中的資源文件 ;
一、加載插件包資源文件
在插件包中的 Activity , 如果加載 R.layout.activity_main , 拿到的是 “宿主” 應用中的資源 , 無法拿到插件包中的資源 ;
1、反射 AssetManager 類并創建實例對象
反射 AssetManager 類 , 并創建 AssetManager 實例對象 ;
// 使用反射工具類進行鏈式調用 , 創建 AssetManager 對象AssetManager assetManager = Reflector.on(AssetManager.class).newInstance();涉及到的反射工具類內容 : 上述代碼使用了上一篇博客 【Android 插件化】Hook 插件化框架 ( 反射工具類 | 反射常用操作整理 ) 中的 反射工具類 , 涉及到的反射代碼如下 :
/*** 封裝反射相關邏輯的工具類* 該封裝類會維持鏈式調用*/ public class Reflector {/*** 反射某個類的入口方法** @param type 要反射的類* @return*/public static Reflector on(Class<?> type) {Reflector reflector = new Reflector();reflector.mClass = type;return reflector;}/*** 創建 mClass 類型的實例對象* @param <T>* @return* @throws Exception*/public <T> T newInstance() {try {return (T) mClass.newInstance();} catch (IllegalAccessException e) {e.printStackTrace();return null;} catch (InstantiationException e) {e.printStackTrace();return null;}} }2、反射 AssetManager 的 addAssetPath 方法并調用
通過反射獲取 AssetManager 的 addAssetPath 方法 , 方法的參數是 String 類型 ;
執行該方法 , 傳入當前插件包 APK 的路徑 ;
// 獲取插件包 APK 文件路徑 , 加載該 APK 下的資源// /data/user/0/com.example.plugin_hook/files/plugin.apkString pluginPath = mBase.getFilesDir() + "/plugin.apk";// 使用反射調用 AssetManager 中的 addAssetPath 方法 , 傳入 APK 插件包的路徑// addAssetPath 方法的參數為 /data/user/0/com.example.plugin_hook/files/plugin.apkReflector.on(assetManager).method("addAssetPath", String.class).call(pluginPath);涉及到的反射工具類內容 : 上述代碼使用了上一篇博客 【Android 插件化】Hook 插件化框架 ( 反射工具類 | 反射常用操作整理 ) 中的 反射工具類 , 涉及到的反射代碼如下 :
/*** 封裝反射相關邏輯的工具類* 該封裝類會維持鏈式調用*/ public class Reflector {/*** 反射某個類的入口方法** @param type 要反射的類* @return*/public static Reflector on(Class<?> type) {Reflector reflector = new Reflector();reflector.mClass = type;return reflector;}/*** 反射某個類的入口方法** @param className 要反射的類名* @return*/public static Reflector on(String className) {try {return on(Class.forName(className));} catch (ClassNotFoundException e) {e.printStackTrace();return null;}}/*** 反射某個類的入口方法** @param object 反射類對應的實例對象* @return*/public static Reflector on(Object object) {return on(object.getClass()).with(object);}/*** 反射類中的某個方法** @param name* @param args* @return*/public Reflector method(String name, Class<?>... args) {mMethod = findMethod(name, args);mMethod.setAccessible(true);return this;}/*** 根據方法名 和 參數名稱 , 查找 Method 方法* 首先在本類中查找* 如果找到直接返回字段* 如果在本類中沒有找到 , 就去遍歷它的父類 , 嘗試在父類中查找該字段* 如果有父類 , 則在父類中查找* 如果在父類中找到 , 返回該字段* 如果在父類中沒有找到 , 則返回空* 如果沒有父類 , 返回空** 盡量傳具體的正確的類 , 不要傳子類* @param name* @param args* @return*/private Method findMethod(String name, Class<?>... args) {try {// 首先在本類中查找 , 如果找到直接返回方法return mClass.getDeclaredMethod(name, args);} catch (NoSuchMethodException e) {// 如果在本類中沒有找到 , 就去遍歷它的父類 , 嘗試在父類中查找該方法for (Class<?> cls = mClass; cls != null; cls = cls.getSuperclass()) {try {// 如果在父類中找到 , 返回該字段return cls.getDeclaredMethod(name);} catch (NoSuchMethodException ex) {// 如果在父類中沒有找到 , 則返回空return null;}}// 如果沒有父類, 則返回空return null;}}/*** 調用 mCaller 的 mMethod 方法** @param args* @param <T>* @return*/public <T> T call(Object... args) {try {return (T) mMethod.invoke(mCaller, args);} catch (IllegalAccessException e) {e.printStackTrace();return null;} catch (InvocationTargetException e) {e.printStackTrace();return null;}} }3、創建 Resources
創建 Resources , 傳入 AssetManager 對象作為主要參數 , 其它參數都可以從應用的 Resources 中獲取 ;
// 創建 Resources 并返回return mResources = new Resources(assetManager,mBase.getResources().getDisplayMetrics(),mBase.getResources().getConfiguration()二、自定義 Application 中設置插件資源
加載到插件資源后 , 重寫 Application 的 public Resources getResources() 方法 , 返回插件包方法 ;
@Overridepublic Resources getResources() {if (pluginResources != null)return pluginResources;return super.getResources();}完整代碼示例 :
package com.example.plugin_hook;import android.app.Application; import android.content.res.Resources; import android.util.Log;import java.io.File;import kim.hsl.plugin.PluginManager;public class MyApplication extends Application {private static final String TAG = "plugin_MyApplication";/*** 插件資源*/private Resources pluginResources;@Overridepublic void onCreate() {super.onCreate();// 如果已經存在文件, 先刪除 , 防止拷貝過程中出錯File pluginFile = new File(getFilesDir() + "/plugin.apk");if (pluginFile.exists()){pluginFile.delete();}// 先將 assets 中的插件包拷貝到 內置存儲中CommandUtils.copyAssets2File(this,"plugin.apk",getFilesDir() + "/plugin.apk");// 將文件從 assets/plugin.apk 拷貝到 /data/user/0/com.example.plugin_hook/files/plugin.apkLog.i(TAG, "將文件從 assets/plugin.apk 拷貝到 " + getFilesDir() + "/plugin.apk");// 初始化插件包PluginManager.getInstance(this).init();Log.i(TAG, "插件化 初始化完畢");// 設置插件包中的資源文件pluginResources = PluginManager.getInstance(this).getmResources();}@Overridepublic Resources getResources() {if (pluginResources != null)return pluginResources;return super.getResources();} }三、插件 Activity 組件中設置插件資源
在 Activity 中重寫 public Resources getResources() 方法 , 核心是返回插件資源 ;
@Overridepublic Resources getResources() {if (getApplication() != null && getApplication().getResources() != null) {return getApplication().getResources();}return super.getResources();}完整代碼示例 :
package com.example.plugin;import androidx.appcompat.app.AppCompatActivity;import android.app.Activity; import android.content.res.Resources; import android.os.Bundle; import android.util.Log;public class MainActivity extends Activity {private static final String TAG = "plugin_MainActivity";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Log.i("plugin", "啟動了插件 Activity");}public void log(){Log.i(TAG, "Plugin MainActivity");}@Overridepublic Resources getResources() {if (getApplication() != null && getApplication().getResources() != null) {return getApplication().getResources();}return super.getResources();} }四、博客資源
博客資源 :
- GitHub : https://github.com/han1202012/Plugin_Hook
總結
以上是生活随笔為你收集整理的【Android 插件化】Hook 插件化框架 ( 加载插件包资源 )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Android 插件化】Hook 插件
- 下一篇: 【Android 插件化】Hook 插件