android 如何動態的加載類----app插件技術

前言:

      在目前的軟硬件環境下,Native App與Web App在用戶體驗上有着明顯的優勢,但在實際項目中有些會因爲業務的頻繁變更而頻繁的升級客戶端,造成較差的用戶體驗,而這也恰恰是Web App的優勢。現如今很多項目要求需要採用類似於微信或Q遊這樣的插件化開發模式越來越多,本文就是闡述android的動態加載技術來滿足插件化開發模式的文章。

1.基本概念

1.1  在Android中可以動態加載,但無法像Java中那樣方便動態加載jar。

Android的虛擬機(DalvikVM)是不認識Java打出jar的byte code,需要通過dx工具來優化轉換成Dalvikbyte code才行。這一點在咱們Android項目打包的apk中可以看出:引入其他Jar的內容都被打包進了classes.dex。即android要加載的java類必須dex格式的代碼文件.

1.2  在Android中可以加載基於NDK的so庫。

NDK的執行效率很高,加密性很好,但同時開發入門難度大,一般用於加解密、數學運算等場合。so的加載很簡單,如果APK發佈時已經攜帶了so文件,只需要在加載時調用System.loadLibrary(libName)方法即可。由於軟件的安裝目錄中存放so的目錄是沒有寫權限的,開發者不能更改該目錄的內容,所以如果要動態加載存放在其他地方的so文件,用System.load(pathName)方法即可。

1.3  在Android中支持動態加載dex文件的兩種方式:

DexClassLoader這個可以加載jar/apk/dex,也可以從SD卡中加載,也是本文的重點

PathClassLoader只能加載已經安裝到Android系統中的apk文件。也就是 /data/app 目錄下的 apk 文件。其它位置的文件加載的時候都會出現 ClassNotFoundException.因爲 PathClassLoader 會去讀取 /data/dalvik-cache 目錄下的經過 Dalvik 優化過的 dex 文件,這個目錄的 dex 文件是在安裝 apk 包的時候由 Dalvik 生成的。


2.注意

2.1 採用不用安裝的插件開發模式,只能夠使用 DexClassLoader進行加載.不過動態加載是有一些限制的,比如插件(子apk)包中的Activity、Service類是不能動態加載的,因爲缺少聲明;即使你在Manifest文件中進行了聲明,系統默認也是到安裝apk所在的路徑中去尋找類,所以你會遇到一個ClassNotFound的異常。插件裏你可以用主apk中先前放入的layout、strings等資源。但是插件中自帶的界面只能用純代碼進行編寫,插件中是不能加載插件(子apk)中的xml作爲layout等資源使用的。所以在開發上一些特效會比較困難些,建議預先植入主apk中。

2.2 大家可以看看DexClassLoader的API文檔,裏面不提倡從SD卡加載,不安全

3.如何製作插件

3.1 把工程導出爲jar包

3.2 執行SDK安裝目錄android-sdk-windows\platform-tools下的dx命令,把jar包轉換爲dex格式

dx --dex --output=dex名 jar包名

4.如何做到啓動未安裝的apk中的activity?

   採用反射機制,把主apk中的activity的context傳遞到插件的activity中,然後採用反射進行回調插件activity的方法。不足之出就是,插件中的activity並不是真正的activity,它只是運行在主activity中。比如:點擊返回直接退出當前activity而不是回到主activity。實例如下:

這是調用的Activity:

[java] view plaincopy
  1. package com.beyondsoft.activity;  
  2.   
  3. import java.lang.reflect.Constructor;  
  4. import java.lang.reflect.InvocationTargetException;  
  5. import java.lang.reflect.Method;  
  6.   
  7. import dalvik.system.DexClassLoader;  
  8. import android.app.Activity;  
  9. import android.content.pm.PackageInfo;  
  10. import android.os.Bundle;  
  11. import android.util.Log;  
  12.   
  13. public class PlugActivity extends Activity {  
  14.   
  15.     private Class mActivityClass;  
  16.     private Object mActivityInstance;  
  17.     Class localClass;  
  18.     private Object instance;  
  19.   
  20.     @Override  
  21.     protected void onCreate(Bundle savedInstanceState) {  
  22.         super.onCreate(savedInstanceState);  
  23.   
  24.         Bundle paramBundle = new Bundle();  
  25.         paramBundle.putBoolean("KEY_START_FROM_OTHER_ACTIVITY"true);  
  26.         paramBundle.putString("str""PlugActivity");  
  27.         String dexpath = "/sdcard/FragmentProject.apk";  
  28.         String dexoutputpath = "/mnt/sdcard/";  
  29.         LoadAPK(paramBundle, dexpath, dexoutputpath);  
  30.     }  
  31.   
  32.     @Override  
  33.     protected void onStart() {  
  34.         super.onStart();  
  35.         Method start;  
  36.         try {  
  37.             start = localClass.getMethod("onStart");  
  38.                 start.invoke(instance);  
  39.         } catch (Exception e) {  
  40.             // TODO Auto-generated catch block  
  41.             e.printStackTrace();  
  42.         }  
  43.     }  
  44.   
  45.     @Override  
  46.     protected void onResume() {  
  47.         // TODO Auto-generated method stub  
  48.         super.onResume();  
  49.         Method resume;  
  50.         try {  
  51.             resume = localClass.getMethod("onResume");  
  52.             resume.invoke(instance);  
  53.         } catch (Exception e) {  
  54.             // TODO Auto-generated catch block  
  55.             e.printStackTrace();  
  56.         }  
  57.     }  
  58.   
  59.     @Override  
  60.     protected void onPause() {  
  61.         super.onPause();  
  62.         Method pause;  
  63.         try {  
  64.             pause = localClass.getMethod("onPause");  
  65.             pause.invoke(instance);  
  66.         } catch (Exception e) {  
  67.             e.printStackTrace();  
  68.         }  
  69.     }  
  70.   
  71.     @Override  
  72.     protected void onStop() {  
  73.         super.onStop();  
  74.         try {  
  75.             Method stop = localClass.getMethod("onStop");  
  76.             stop.invoke(instance);  
  77.         } catch (Exception e) {  
  78.             e.printStackTrace();  
  79.         }  
  80.     }  
  81.   
  82.     @Override  
  83.     protected void onDestroy() {  
  84.         // TODO Auto-generated method stub  
  85.         super.onDestroy();  
  86.         try {  
  87.             Method des = localClass.getMethod("onDestroy");  
  88.             des.invoke(instance);  
  89.         } catch (Exception e) {  
  90.             // TODO Auto-generated catch block  
  91.             e.printStackTrace();  
  92.         }  
  93.     }  
  94.   
  95.     public void LoadAPK(Bundle paramBundle, String dexpath, String dexoutputpath) {  
  96.         ClassLoader localClassLoader = ClassLoader.getSystemClassLoader();  
  97.         DexClassLoader localDexClassLoader = new DexClassLoader(dexpath, dexoutputpath, null, localClassLoader);  
  98.         try {  
  99.             PackageInfo plocalObject = getPackageManager().getPackageArchiveInfo(dexpath, 1);  
  100.   
  101.             if ((plocalObject.activities != null) && (plocalObject.activities.length > 0)) {  
  102.                 String activityname = plocalObject.activities[0].name;  
  103.                 Log.d("sys""activityname = " + activityname);  
  104.   
  105.                 localClass = localDexClassLoader.loadClass(activityname);//結果:"com.example.fragmentproject.FristActivity"  
  106.                 mActivityClass = localClass;  
  107.                 Constructor localConstructor = localClass.getConstructor(new Class[] {});  
  108.                 instance = localConstructor.newInstance(new Object[] {});  
  109.                 Log.d("sys""instance = " + instance);  
  110.                 mActivityInstance = instance;  
  111.   
  112.                 Method des = localClass.getMethod("test");  
  113.                 des.invoke(instance);  
  114.                   
  115.                 Method localMethodSetActivity = localClass.getDeclaredMethod("setActivity"new Class[] { Activity.class });  
  116.                 localMethodSetActivity.setAccessible(true);  
  117.                 localMethodSetActivity.invoke(instance, new Object[] { this });  
  118.   
  119.                  Method methodonCreate = localClass.getDeclaredMethod("onCreate"new Class[] { Bundle.class });  
  120.                  methodonCreate.setAccessible(true);  
  121.                  methodonCreate.invoke(instance, paramBundle);  
  122.             }  
  123.             return;  
  124.         } catch (Exception ex) {  
  125.             ex.printStackTrace();  
  126.         }  
  127.     }  
  128.   
  129. }  


這是被調用的Activity:

[java] view plaincopy
  1. public class FristActivity extends Activity{  
  2.   
  3.     private Button fragment;  
  4.     private Button listFragment;  
  5.     private Button controlFragment;  
  6.     private Button viewFlipper;  
  7.     private Button viewPager;  
  8.     private Activity otherActivity;  
  9.   
  10.     public void test() {  
  11.         Log.i("sys""測試方法執行了");  
  12.     }  
  13.   
  14.     @Override  
  15.     public void onCreate(Bundle savedInstanceState) {  
  16.         super.onCreate(savedInstanceState);  
  17.          // 測試DexClassLoader 動態加載未安裝Apk中的類  
  18.         TextView t = new TextView(otherActivity);  
  19.         t.setText("我是測試");  
  20.         otherActivity.setContentView(t);// R.layout.frist_activity_main  
  21.   
  22.         Log.i("sys""Fragment項目啓動了");  
  23.     }  
  24.   
  25.     public void setActivity(Activity paramActivity) {  
  26.         Log.d("sys""setActivity..." + paramActivity);  
  27.         this.otherActivity = paramActivity;  
  28.     }  
  29.   
  30.     @Override  
  31.     public void onSaveInstanceState(Bundle outState) {  
  32.         super.onSaveInstanceState(outState);  
  33.         Log.i("sys""OnSaveInstance被調了");  
  34.     }  
  35.   
  36.     @Override  
  37.     public void onStart() {  
  38.         Log.i("sys""onStart被調了");  
  39.         // TODO Auto-generated method stub  
  40.         super.onStart();  
  41.     }  
  42.   
  43.     @Override  
  44.     public void onResume() {  
  45.         Log.i("sys""onResume被調了");  
  46.         // TODO Auto-generated method stub  
  47.         super.onResume();  
  48.     }  
  49.   
  50.     @Override  
  51.     public void onPause() {  
  52.         Log.i("sys""onPause被調了");  
  53.         // TODO Auto-generated method stub  
  54.         super.onPause();  
  55.     }  
  56.   
  57.     @Override  
  58.     public void onStop() {  
  59.         Log.i("sys""onStop被調了");  
  60.         // TODO Auto-generated method stub  
  61.         super.onStop();  
  62.     }  
  63.   
  64.     @Override  
  65.     protected void onDestroy() {  
  66.         Log.i("sys""onDestroy被調了");  
  67.         // TODO Auto-generated method stub  
  68.         super.onDestroy();  
  69.     }  
  70. }  
5.參考文章

1.http://www.cnblogs.com/over140/archive/2011/11/23/2259367.html

2.http://blog.csdn.net/mirkerson/article/details/8771723

3.http://blog.csdn.net/scliu0718/article/details/8438823

4.http://www.verydemo.com/demo_c131_i24569.htmlAndroid 通過反射啓動未安裝的APK中的Activity的實例代碼)

5.http://www.myexception.cn/android/1217391.htmlAndroid 通過反射啓動未安裝的APK中的Activity的實例圖形說明)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章