外掛化載入未安裝APK
主程式專門開一個PluginActivity,在該Activity裡通過dexClassLoader動態載入apk,通過反射的方式向被載入apk的mainActivity裡傳遞PluginAvtivity這個引數,相當於在主程式的PluginActivity裡畫Apk中Activity的View。
PluginActivity中的核心方法:loadApk
DexClassLoader這個類載入器用來從.jar和.apk型別的檔案內部載入classes.dex檔案。通過這種方式可以用來執行非安裝的程式程式碼,作為程式的一部分進行執行。這個裝載類需要一個程式私有的,可寫的檔案目錄去存放優化後的classes檔案。通過Contexct.getDir(String, int)來建立這個目錄:File dexOutputDir = context.getDir("dex", 0);
public File getDir(String name, int mode):name目錄名稱、mode許可權,如果傳入的目錄不存在,系統會建立此目錄,路徑為"/data/data/程式Package Name/app_name",name就是傳入的name。
通過以下程式碼來獲得DexClassLoader。dexPath是需要裝載的APK的路徑
DexClassLoader localDexClassLoader = new DexClassLoader(dexpath, dexOutputDir.getAbsolutePath(), null, ClassLoader.getSystemClassLoader().getParent());
通過以下程式碼獲得應用資訊
PackageInfo pkgInfo = getPackageManager().getPackageArchiveInfo(dexpath,
PackageManager.GET_ACTIVITIES);
if ((pkgInfo.activities != null) && (pkgInfo.activities.length > 0)) { String activityname = pkgInfo.activities[0].name; //獲得應用資訊裡的第一個Activity名 localClass = localDexClassLoader.loadClass(activityname);//通過反射載入型別 mActivityClass = localClass; Constructor localConstructor = localClass.getConstructor(new Class[] {});//獲得構造方法 instance = localConstructor.newInstance(new Object[] {}); //建立物件例項 mActivityInstance = instance; Method localMethodSetActivity = localClass.getDeclaredMethod("setActivity", new Class[] { Activity.class }); localMethodSetActivity.setAccessible(true); localMethodSetActivity.invoke(instance, new Object[] { this }); //相當於呼叫MainActivity裡的setActivity方法,把PluginActivity傳遞了進去
此外,還需要獲取未安裝apk的資源——getApkResoures方法
反射呼叫android.content.res.AssetManager類,新建個例項,呼叫隱藏的方法addAssetPath(String path)將為安裝APK檔案的新增進去,然後用這個AssetManager來構建出一個Resource例項
try {
Class<?> class_AssetManager = Class.forName("android.content.res.AssetManager");
Object assetMag = class_AssetManager.newInstance();
Method method_addAssetPath = class_AssetManager
.getDeclaredMethod("addAssetPath", String.class);
String path = 路徑;
String fileName = 檔名;
method_addAssetPath.invoke(assetMag, path + fileName);
Resources res = context.getResources();
Constructor<?> constructor_Resources = Resources.class
.getConstructor(class_AssetManager, res.getDisplayMetrics()
.getClass(), res.getConfiguration().getClass());
res = (Resources) constructor_Resources.newInstance(assetMag,
res.getDisplayMetrics(), res.getConfiguration());
} catch (Exception e) {
e.printStackTrace();
}
在MainActivity裡設一個setResources方法,反射呼叫它,把getApkResoures方法返回的Resource例項傳遞進去。
最後反射呼叫MainActivity的onCreate方法
Method methodonCreate = localClass.getDeclaredMethod("onCreate", new Class[] { Bundle.class });
methodonCreate.setAccessible(true);
methodonCreate.invoke(instance, savedInstanceState);
二,如何更換應用主題時同時同步到外掛裡去?
外掛載入資源時先判斷Activity的資源裡有沒有它,有就呼叫,沒有再呼叫自己的Resource