Android動態載入代碼技術

來源:互聯網
上載者:User

      在開發Android App的過程當中,可能希望實現外掛程式式軟體架構,將一部分代碼以另外一個APK的形式單獨發布,而在主程式中載入並執行這個APK中的代碼。

      實現這個任務的一般方法是:

// 載入類clsContext pluginContext = mainContext.createPackageContext(PLUGIN_PKG, Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE);ClassLoader loader = pluginContext.getClassLoader();Class<?> cls = loader.loadClass(CLASS_NAME);// 通過反射技術,調用cls中的方法,下面是一個樣本,實際代碼因情況而定Object obj = cls.newInstance();Method method = cls.getDeclaredMethod("someMethod");method.invoke(obj);

      但是,這個方法在Android 4.1及之後的系統中存在一些問題:對於收費應用,Google Play會將其安裝在一個加密目錄之下(具體就是/data/app-asec),而不是一個普通目錄之下(具體就是/data/app);安裝在加密目錄中的應用,我們是無法使用上述方法來載入並執行代碼的;而實際情況是,我們經常就是依靠外掛程式應用來收費的。

      解決上述問題的一個方案是:將外掛程式的二進位代碼拷貝到SD卡中,主程式從SD卡中載入並執行其代碼。

      實現這個任務的具體方法是:

Class<?> cls = null;try {
// 嘗試第一種方法 cls = loadClass1(mainContext, pkg, entryCls);} catch (Exception e) {
// 嘗試第二種方法 cls = loadClass2(mainContext, pkg, entryCls);}
// 範例程式碼
Object obj = cls.newInstance();
Method method = cls.getDeclaredMethod("someMethod");
method.invoke(obj);
// 第一種載入方法
private Class<?> loadClass1(Context mainContext, String pkg, String entryCls) throws Exception {
Context pluginContext = mainContext.createPackageContext(pkg, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
ClassLoader loader = pluginContext.getClassLoader();
return loader.loadClass(entryCls);
}

// 第二種載入方法
private Class<?> loadClass2(Context mainContext, String pkg, String entryCls) throws Exception {
Context pluginContext = mainContext.createPackageContext(pkg, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
String path = generatePluginDexPath(mainContext, pkg);
ensureFileExist(pluginContext, pkg, path);
// cacheDir必須是主程式的私人目錄,否則DexClassLoader可能會拒絕載入
String cacheDir = mainContext.getApplicationInfo().dataDir;
ClassLoader parentLoader = pluginContext.getClassLoader();
DexClassLoader loader = new DexClassLoader(path, cacheDir, null, parentLoader);
return loader.loadClass(entryCls);
}

// 擷取程式版本號碼
private int getVersionCode(Context context, String pkg) {
PackageInfo info = null;
int versionCode = 0;
try {
info = context.getPackageManager().getPackageInfo(pkg, PackageManager.GET_ACTIVITIES);
versionCode = info.versionCode;
} catch (Exception e) {}
return versionCode;
}

// 擷取外掛程式二進位代碼的儲存位置,注意做好版本控制;路徑必須是以.dex結束,否則載入會出問題
private String generatePluginDexPath(Context context, String pkg) {
int version = getVersionCode(context, pkg);
String path = getMyAppPath() + ".classes/" + pkg + version + ".dex";
return path;
}

// 主程式在SD卡上的資料目錄
private String getMyAppPath() {
return Environment.getExternalStorageDirectory().getAbsolutePath() + "/MyApp/";
}
// 拷貝外掛程式的二進位代碼到SD卡
private void ensureFileExist(Context pluginContext, String pkg, String path) throws Exception {
File file = new File(path);
if(file.exists()) return;
file.getParentFile().mkdirs();
Resources res = pluginContext.getResources();
int id = res.getIdentifier("classes", "raw", pkg);
InputStream in = res.openRawResource(id);
FileOutputStream out = new FileOutputStream(file);
try {
byte[] buffer = new byte[1024 * 1024];
int n = 0;
while((n = in.read(buffer)) > 0) {
out.write(buffer, 0, n);
} out.flush();
} catch (IOException e) {
in.close();
out.close();
}
}

 

      外掛程式工程這邊也需要做相應的修改:

      1.編譯外掛程式工程;

      2.將bin目錄之下的classes.dex拷貝到/res/raw目錄之下;

      3.重新編譯外掛程式工程;

      4.發布外掛程式APK。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.