1. 程式人生 > >android activity動態載入

android activity動態載入

通過分析QQGame的專案,發現其存在兩種方式:

1. 不安裝遊戲apk,直接啟動

其原理是:

  1. 把apk裡的class檔案通過DexClassLoader把apk裡的class檔案全部載入到java虛擬機器裡,如果要使用其中的某個class時,就要使用反射來呼叫。

  2. 如果這個類是Activity的子類,那如何來啟動,Activity的子類是由android系統來建立,處理方法是:把Activity的子類當做一個有著Activity相應介面的類,在專案裡建立一個空的Activity類,其裡面不做任何事情,只是用反射呼叫真正的Activity的方法,程式碼如下:

Java程式碼  收藏程式碼
  1. public class MainActivity extends Activity {  
  2.     private static final String TAG = "MainActivity";  
  3.     private Class mActivityClass;  
  4.     private Object mActivityInstance;  
  5.     @Override  
  6.     public void onCreate(Bundle savedInstanceState) {  
  7.         super.onCreate(savedInstanceState);  
  8.         setContentView(R.layout.main);  
  9.         Button btn = (Button) findViewById(R.id.btn);  
  10.         btn.setOnClickListener(new OnClickListener() {  
  11.             @Override  
  12.             public void onClick(View v) {  
  13.                 Bundle paramBundle = new Bundle();  
  14.                 paramBundle.putBoolean("KEY_START_FROM_OTHER_ACTIVITY"
    true);  
  15.                 String dexpath = "/mnt/sdcard/TestB.apk";  
  16.                 String dexoutputpath = "/mnt/sdcard/";  
  17.                 LoadAPK(paramBundle, dexpath, dexoutputpath);  
  18.             }  
  19.         });  
  20.     }  
  21.     @Override  
  22.     protected void onStart() {  
  23.         super.onStart();  
  24.         try {  
  25.             Method method = mActivityClass.getDeclaredMethod(  
  26.                     "onStart");  
  27.             method.setAccessible(true);  
  28.             method.invoke(mActivityInstance);  
  29.         } catch (Exception e) {  
  30.             e.printStackTrace();  
  31.         }  
  32.     }  
  33.     @Override  
  34.     protected void onResume() {  
  35.         super.onResume();  
  36.         try {  
  37.             Method method = mActivityClass.getDeclaredMethod(  
  38.                     "onResume");  
  39.             method.setAccessible(true);  
  40.             method.invoke(mActivityInstance);  
  41.         } catch (Exception e) {  
  42.             e.printStackTrace();  
  43.         }  
  44.     }  
  45.     @Override  
  46.     protected void onPause() {  
  47.         super.onPause();  
  48.         try {  
  49.             Method method = mActivityClass.getDeclaredMethod(  
  50.                     "onPause");  
  51.             method.setAccessible(true);  
  52.             method.invoke(mActivityInstance);  
  53.         } catch (Exception e) {  
  54.             e.printStackTrace();  
  55.         }  
  56.     }  
  57.     @Override  
  58.     protected void onStop() {  
  59.         super.onStop();  
  60.         try {  
  61.             Method method = mActivityClass.getDeclaredMethod(  
  62.                     "onStop");  
  63.             method.setAccessible(true);  
  64.             method.invoke(mActivityInstance);  
  65.         } catch (Exception e) {  
  66.             e.printStackTrace();  
  67.         }  
  68.     }  
  69.     @Override  
  70.     protected void onDestroy() {  
  71.         super.onDestroy();  
  72.         try {  
  73.             Method method = mActivityClass.getDeclaredMethod(  
  74.                     "onDestroy");  
  75.             method.setAccessible(true);  
  76.             method.invoke(mActivityInstance);  
  77.         } catch (Exception e) {  
  78.             e.printStackTrace();  
  79.         }  
  80.     }  
  81.     public void LoadAPK(Bundle paramBundle, String dexpath, String dexoutputpath) {  
  82.         ClassLoader localClassLoader = ClassLoader.getSystemClassLoader();  
  83.         DexClassLoader localDexClassLoader = new DexClassLoader(dexpath,  
  84.                 dexoutputpath, null, localClassLoader);  
  85.         try {  
  86.             PackageInfo plocalObject = getPackageManager()  
  87.                     .getPackageArchiveInfo(dexpath, 1);  
  88.             if ((plocalObject.activities != null)  
  89.                     && (plocalObject.activities.length > 0)) {  
  90.                 String activityname = plocalObject.activities[0].name;  
  91.                 Log.d(TAG, "activityname = " + activityname);  
  92.                 Class localClass = localDexClassLoader.loadClass(activityname);  
  93.                 mActivityClass = localClass;  
  94.                 Constructor localConstructor = localClass  
  95.                         .getConstructor(new Class[] {});  
  96.                 Object instance = localConstructor.newInstance(new Object[] {});  
  97.                 Log.d(TAG, "instance = " + instance);  
  98.                 mActivityInstance = instance;  
  99.                 Method localMethodSetActivity = localClass.getDeclaredMethod(  
  100.                         "setActivity"new Class[] { Activity.class });  
  101.                 localMethodSetActivity.setAccessible(true);  
  102.                 localMethodSetActivity.invoke(instance, new Object[] { this });  
  103.                 Method methodonCreate = localClass.getDeclaredMethod(  
  104.                         "onCreate"new Class[] { Bundle.class });  
  105.                 methodonCreate.setAccessible(true);  
  106.                 methodonCreate.invoke(instance, new Object[] { paramBundle });  
  107.             }  
  108.             return;  
  109.         } catch (Exception ex) {  
  110.             ex.printStackTrace();  
  111.         }  
  112.     }  
  113. }  

 從上面可以看到,由於我們不能像系統一樣初始化Activity,所以只能用一個Activity做為了容器,來呼叫其Activity相應的生命週期方法。

在另外的Activity裡,把顯示的view設定到傳過來的Activity裡面,同時,所有要與系統互動的,都要通過傳過來的activity物件,這個和我們最開始的那個框架差不多。

這樣做的好處:

  1. 可以做到apk,不安裝的情況下,就可以啟動這個apk

  2. 對於qqgame,由於只有一個Activity,而且遊戲的圖片資源都是自己載入的,所以很適合用這種方式

不足之處:

  1. 不能把res裡的資原始檔交給系統來管理,也就是資源(圖片等等),都要自己去sdcard裡去讀取,去維護。

  2. 如果有多個Activity時,就會很麻煩,由於只是把些類載入進來,但不能由系統來初始化,那些startActivity的方法基本上不能用(雖然可以模擬,但會有很多的問題)

  3. 由於有這種限制,所以QQ的專案裡,現在還只有QQGame用了這種模式

2. 安裝遊戲apk,再進行啟動

這種模式比較簡單:

1. 不要用啟動的Activity

2. 把第一個啟動的Activity加一個約定的Catergory:

Xml程式碼  收藏程式碼
  1. <category android:name="android.intent.category.XXXX" />  

還後,在門戶的專案裡,對當前手機進行探測,看有多少個含有這個類別的Activity,可以進行顯示出來了。

結論:

對了我們這種應用app的程式,用這種方式,會大大加大開發的複雜度,不過其第二種方法不錯,但要變化著使用。

使用交叉推薦:

  1. 在每個不同的專案裡,都加入對其他產品的推薦(同時,檢測使用者手機裡已經安裝過的apk)。

  2. 有一些功能,還可以讓使用者跳到其他的apk實現。