TShopping

 找回密碼
 註冊
搜索
查看: 1480|回復: 0
打印 上一主題 下一主題

[教學] Android動態加載代碼技術

[複製鏈接]
跳轉到指定樓層
1#
發表於 2013-8-23 17:52:06 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
 
Push to Facebook
在開發Android App的過程當中,可能希望實現插件式軟件架構,將一部分代碼以另外一個APK的形式單獨發佈,而在主程序中加載並執行這個APK中的代碼。      實現這個任務的一般方法是:
  1. // 加載類cls
  2. Context pluginContext = mainContext.createPackageContext(PLUGIN_PKG, Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE);
  3. ClassLoader loader = pluginContext.getClassLoader();
  4. Class<?> cls = loader.loadClass(CLASS_NAME);
  5. // 通過反射技術,調用cls中的方法,下面是一個示例,實際代碼因情況而定
  6. Object obj = cls.newInstance();
  7. Method method = cls.getDeclaredMethod("someMethod");
  8. method.invoke(obj);
複製代碼
但是,這個方法在Android 4.1及之後的系統中存在一些問題:對於收費應用,Google Play會將其安裝在一個加密目錄之下(具體就是/data/app-asec),而不是一個普通目錄之下(具體就是/data/app);安裝在加密目錄中的應用,我們是無法使用上述方法來加載並執行代碼的;而實際情況是,我們經常就是依靠插件應用來收費的。
      解決上述問題的一個方案是:將插件的二進制代碼拷貝到SD卡中,主程序從SD卡中加載並執行其代碼。
      實現這個任務的具體方法是:
  1. Class<?> cls = null;
  2. try {
  3.      // 嘗試第一種方法
  4.      cls = loadClass1(mainContext, pkg, entryCls);
  5. } catch (Exception e) {
  6.      // 嘗試第二種方法
  7.      cls = loadClass2(mainContext, pkg, entryCls);
  8. }
  9. // 示例代碼
  10. Object obj = cls.newInstance();
  11. Method method = cls.getDeclaredMethod("someMethod");
  12. method.invoke(obj);// 第一種加載方法
  13. private Class<?> loadClass1(Context mainContext, String pkg, String entryCls) throws Exception {
  14.     Context pluginContext = mainContext.createPackageContext(pkg, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
  15.     ClassLoader loader = pluginContext.getClassLoader();
  16.     return loader.loadClass(entryCls);
  17. }

  18. // 第二種加載方法
  19. private Class<?> loadClass2(Context mainContext, String pkg, String entryCls) throws Exception {
  20.     Context pluginContext = mainContext.createPackageContext(pkg, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
  21.     String path = generatePluginDexPath(mainContext, pkg);
  22.     ensureFileExist(pluginContext, pkg, path);
  23.     // cacheDir必須是主程序的私有目錄,否則DexClassLoader可能會拒絕加載
  24.     String cacheDir = mainContext.getApplicationInfo().dataDir;
  25.     ClassLoader parentLoader = pluginContext.getClassLoader();
  26.     DexClassLoader loader = new DexClassLoader(path, cacheDir, null, parentLoader);
  27.     return loader.loadClass(entryCls);
  28. }

  29. // 獲取程序版本號
  30. private int getVersionCode(Context context, String pkg) {
  31.     PackageInfo info = null;
  32.     int versionCode = 0;
  33.     try {
  34.          info = context.getPackageManager().getPackageInfo(pkg, PackageManager.GET_ACTIVITIES);
  35.          versionCode = info.versionCode;
  36.     } catch (Exception e) {}
  37.     return versionCode;
  38. }

  39. // 獲取插件二進制代碼的存儲位置,注意做好版本控制;路徑必須是以.dex結束,否則加載會出問題
  40. private String generatePluginDexPath(Context context, String pkg) {
  41.     int version = getVersionCode(context, pkg);
  42.     String path = getMyAppPath() + ".classes/" + pkg + version + ".dex";
  43.     return path;
  44. }

  45. // 主程序在SD卡上的數據目錄
  46. private String getMyAppPath() {
  47.     return Environment.getExternalStorageDirectory().getAbsolutePath() + "/MyApp/";
  48. }// 拷貝插件的二進制代碼到SD卡
  49. private void ensureFileExist(Context pluginContext, String pkg, String path) throws Exception {
  50.     File file = new File(path);
  51.     if(file.exists()) return;
  52.     file.getParentFile().mkdirs();
  53.     Resources res = pluginContext.getResources();
  54.     int id = res.getIdentifier("classes", "raw", pkg);
  55.     InputStream in = res.openRawResource(id);
  56.     FileOutputStream out = new FileOutputStream(file);
  57.     try {
  58.          byte[] buffer = new byte[1024 * 1024];
  59.          int n = 0;
  60.          while((n = in.read(buffer)) > 0) {
  61.             out.write(buffer, 0, n);
  62.          } out.flush();
  63.     } catch (IOException e) {
  64.        in.close();
  65.        out.close();
  66.     }
  67. }
複製代碼
插件工程這邊也需要做相應的修改:
      1.編譯插件工程;
      2.將bin目錄之下的classes.dex拷貝到/res/raw目錄之下;
      3.重新編譯插件工程;
      4.發佈插件APK。

http://www.cnblogs.com/frydsh/archive/2012/12/21/2828561.html

 

臉書網友討論
*滑块验证:
您需要登錄後才可以回帖 登錄 | 註冊 |

本版積分規則



Archiver|手機版|小黑屋|免責聲明|TShopping

GMT+8, 2024-4-20 03:22 , Processed in 0.066280 second(s), 22 queries .

本論壇言論純屬發表者個人意見,與 TShopping綜合論壇 立場無關 如有意見侵犯了您的權益 請寫信聯絡我們。

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回復 返回頂部 返回列表