【Android 插件化】“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 )
Android 插件化系列文章目錄
【Android 插件化】插件化簡介 ( 組件化與插件化 )
【Android 插件化】插件化原理 ( JVM 內存數據 | 類加載流程 )
【Android 插件化】插件化原理 ( 類加載器 )
【Android 插件化】“ 插樁式 “ 插件化框架 ( 原理與實現思路 )
【Android 插件化】“ 插樁式 “ 插件化框架 ( 類加載器創建 | 資源加載 )
【Android 插件化】“ 插樁式 “ 插件化框架 ( 注入上下文的使用 )
【Android 插件化】“ 插樁式 “ 插件化框架 ( 獲取插件入口 Activity 組件 | 加載插件 Resources 資源 )
【Android 插件化】“ 插樁式 “ 插件化框架 ( 運行應用 | 代碼整理 )
文章目錄
- Android 插件化系列文章目錄
- 一、創建核心依賴庫
- 二、創建類加載器
- 三、加載資源
- 四、插件管理器完整代碼
- 五、博客資源
參考 【Android 插件化】“ 插樁式 “ 插件化框架 ( 原理與實現思路 ) 中給出的實現思路 , 逐步實現 “ 插樁式 “ 插件化框架 ;
一、創建核心依賴庫
創建 " Android Library " 依賴庫 , 作為 " 插件化 " 框架 核心依賴庫 ;
" 宿主 " 模塊 應用 , 依賴該 “ 插樁式 “ 插件化框架 核心庫 , 依靠該框架核心庫 , 管理 " 插件 " 模塊 編譯打包成的 apk 文件 ;
二、創建類加載器
創建 DexClassLoader , 使用其構造函數創建 , 需要傳入四個參數到構造函數中 ;
package dalvik.system;import java.io.File;public class DexClassLoader extends BaseDexClassLoader {public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {super((String)null, (File)null, (String)null, (ClassLoader)null);throw new RuntimeException("Stub!");} }DexClassLoader 構造函數 參數說明 :
① String dexPath : 插件包加載路徑 ;
② String optimizedDirectory : 開發者指定的 apk 插件包解壓后的緩存路徑 ;
③ String librarySearchPath : 函數庫的搜索路徑 , 可設置為空 , 忽略 ;
④ ClassLoader parent : DexClassLoader 加載器的父類加載器 ;
創建插件包解壓后的緩存路徑 : 注意 String optimizedDirectory 參數對應的路徑必須是私有的 ;
// DexClassLoader 的 optimizedDirectory 操作目錄必須是私有的 // ( 模式必須是 Context.MODE_PRIVATE ) File optimizedDirectory = context.getDir("plugin", Context.MODE_PRIVATE);創建類加載器 : 傳入上述 444 個參數 , 創建類加載器 ;
// 創建 DexClassLoader mDexClassLoader = new DexClassLoader(loadPath, // 加載路徑optimizedDirectory.getAbsolutePath(), // apk 解壓緩存目錄null,context.getClassLoader() // DexClassLoader 加載器的父類加載器 );注意 : 類加載時 , 只會加載一次 , 如果有重復的類 , 不會重復加載 ;
BootClassLoader 主要作用是加載 JDK 中的字節碼類對象 ;
DexClassLoader 和 PathClassLoader 主要作用是加載 Android 和 引入的第三方庫 中的字節碼類對象 ;
三、加載資源
加載資源時需要使用到 AssetManager , 但是其構造函數是 隱藏 的 , 被 @Hide 注解 , 開發者無法直接調用 , 需要使用反射進行調用 ;
通過反射創建 AssetManager 對象 : 注意異常捕獲 ;
// 加載資源 try {// 通過反射創建 AssetManagerAssetManager assetManager = AssetManager.class.newInstance(); } catch (IllegalAccessException e) {e.printStackTrace(); } catch (InstantiationException e) {e.printStackTrace(); }創建 AssetManager 對象后 , 調用 addAssetPath 方法 , 添加資源的路徑 , 用于加載插件包路徑下的資源文件 ;
addAssetPath 也是隱藏方法 , 也是需要使用反射調用該方法 ;
// 通過反射獲取 AssetManager 中的 addAssetPath 隱藏方法 Method addAssetPathMethod = assetManager.getClass().getDeclaredMethod("addAssetPath"); // 調用反射方法 addAssetPathMethod.invoke(assetManager, loadPath);AssetManager 示例代碼 :
public final class AssetManager implements AutoCloseable {/*** @hide*/@UnsupportedAppUsagepublic AssetManager() {}/*** @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)}* @hide*/@Deprecated@UnsupportedAppUsagepublic int addAssetPath(String path) {return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/);} }獲取 Resources 資源對象 : 通過上述加載插件資源后的 AssetManager 對象來創建 Resources 資源對象 ;
// 獲取資源 mResources = new Resources(assetManager,context.getResources().getDisplayMetrics(),context.getResources().getConfiguration() );傳入的 DisplayMetrics metrics 和 Configuration config 參數從調用插件包的上下文中獲取 ;
加載資源部分代碼示例 :
首先 , 通過反射創建 AssetManager 對象 ;
然后 , 通過反射調用并執行 AssetManager 對象的 addAssetPath 方法 , 加載插件包資源 ;
最后 , 調用 Resources 構造函數 , 創建資源 , 傳入 AssetManager 對象 和 上下文相關參數 ;
四、插件管理器完整代碼
插件管理器完整代碼 :
package com.example.plugin_core;import android.content.Context; import android.content.pm.PackageInfo; import android.content.res.AssetManager; import android.content.res.Resources;import java.io.File; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method;import dalvik.system.DexClassLoader;/*** 插件化框架核心類*/ public class PluginManager {/*** 類加載器* 用于加載插件包 apk 中的 classes.dex 文件中的字節碼對象*/private DexClassLoader mDexClassLoader;/*** 從插件包 apk 中加載的資源*/private Resources mResources;/*** 插件包信息類*/private PackageInfo mPackageInfo;/*** 加載插件的上下文對象*/private Context mContext;/*** PluginManager 單例*/private static PluginManager instance;private PluginManager(){}/*** 獲取單例類* @return*/public static PluginManager getInstance(){if (instance == null) {instance = new PluginManager();}return instance;}/*** 加載插件* @param context 加載插件的應用的上下文* @param loadPath 加載的插件包地址*/public void loadPlugin(Context context, String loadPath) {this.mContext = context;// DexClassLoader 的 optimizedDirectory 操作目錄必須是私有的// ( 模式必須是 Context.MODE_PRIVATE )File optimizedDirectory = context.getDir("plugin", Context.MODE_PRIVATE);// 創建 DexClassLoadermDexClassLoader = new DexClassLoader(loadPath, // 加載路徑optimizedDirectory.getAbsolutePath(), // apk 解壓緩存目錄null,context.getClassLoader() // DexClassLoader 加載器的父類加載器);// 加載資源try {// 通過反射創建 AssetManagerAssetManager assetManager = AssetManager.class.newInstance();// 通過反射獲取 AssetManager 中的 addAssetPath 隱藏方法Method addAssetPathMethod = assetManager.getClass().getDeclaredMethod("addAssetPath");// 調用反射方法addAssetPathMethod.invoke(assetManager, loadPath);// 獲取資源mResources = new Resources(assetManager,context.getResources().getDisplayMetrics(),context.getResources().getConfiguration());} catch (IllegalAccessException e) {// 調用 AssetManager.class.newInstance() 反射構造方法異常e.printStackTrace();} catch (InstantiationException e) {// 調用 AssetManager.class.newInstance() 反射構造方法異常e.printStackTrace();} catch (NoSuchMethodException e) {// getDeclaredMethod 反射方法異常e.printStackTrace();} catch (InvocationTargetException e) {// invoke 執行反射方法異常e.printStackTrace();}} }五、博客資源
博客資源 :
- GitHub : https://github.com/han1202012/Plugin
總結
以上是生活随笔為你收集整理的【Android 插件化】“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【错误记录】Android Studio
- 下一篇: 【Android FFMPEG 开发】A