android activity動態載入
通過分析QQGame的專案,發現其存在兩種方式:
1. 不安裝遊戲apk,直接啟動
其原理是:
1. 把apk裡的class檔案通過DexClassLoader把apk裡的class檔案全部載入到java虛擬機器裡,如果要使用其中的某個class時,就要使用反射來呼叫。
2. 如果這個類是Activity的子類,那如何來啟動,Activity的子類是由android系統來建立,處理方法是:把Activity的子類當做一個有著Activity相應介面的類,在專案裡建立一個空的Activity類,其裡面不做任何事情,只是用反射呼叫真正的Activity的方法,程式碼如下:
Java程式碼- public class MainActivity extends Activity {
- private static final String TAG = "MainActivity";
- private Class mActivityClass;
- private Object mActivityInstance;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
setContentView(R.layout.main);
- Button btn = (Button) findViewById(R.id.btn);
- btn.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- Bundle paramBundle = new Bundle();
-
paramBundle.putBoolean("KEY_START_FROM_OTHER_ACTIVITY"
- String dexpath = "/mnt/sdcard/TestB.apk";
- String dexoutputpath = "/mnt/sdcard/";
- LoadAPK(paramBundle, dexpath, dexoutputpath);
- }
- });
- }
- @Override
- protected void onStart() {
- super.onStart();
- try {
- Method method = mActivityClass.getDeclaredMethod(
- "onStart");
- method.setAccessible(true);
- method.invoke(mActivityInstance);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- @Override
- protected void onResume() {
- super.onResume();
- try {
- Method method = mActivityClass.getDeclaredMethod(
- "onResume");
- method.setAccessible(true);
- method.invoke(mActivityInstance);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- @Override
- protected void onPause() {
- super.onPause();
- try {
- Method method = mActivityClass.getDeclaredMethod(
- "onPause");
- method.setAccessible(true);
- method.invoke(mActivityInstance);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- @Override
- protected void onStop() {
- super.onStop();
- try {
- Method method = mActivityClass.getDeclaredMethod(
- "onStop");
- method.setAccessible(true);
- method.invoke(mActivityInstance);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- @Override
- protected void onDestroy() {
- super.onDestroy();
- try {
- Method method = mActivityClass.getDeclaredMethod(
- "onDestroy");
- method.setAccessible(true);
- method.invoke(mActivityInstance);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- public void LoadAPK(Bundle paramBundle, String dexpath, String dexoutputpath) {
- ClassLoader localClassLoader = ClassLoader.getSystemClassLoader();
- DexClassLoader localDexClassLoader = new DexClassLoader(dexpath,
- dexoutputpath, null, localClassLoader);
- try {
- PackageInfo plocalObject = getPackageManager()
- .getPackageArchiveInfo(dexpath, 1);
- if ((plocalObject.activities != null)
- && (plocalObject.activities.length > 0)) {
- String activityname = plocalObject.activities[0].name;
- Log.d(TAG, "activityname = " + activityname);
- Class localClass = localDexClassLoader.loadClass(activityname);
- mActivityClass = localClass;
- Constructor localConstructor = localClass
- .getConstructor(new Class[] {});
- Object instance = localConstructor.newInstance(new Object[] {});
- Log.d(TAG, "instance = " + instance);
- mActivityInstance = instance;
- Method localMethodSetActivity = localClass.getDeclaredMethod(
- "setActivity", new Class[] { Activity.class });
- localMethodSetActivity.setAccessible(true);
- localMethodSetActivity.invoke(instance, new Object[] { this });
- Method methodonCreate = localClass.getDeclaredMethod(
- "onCreate", new Class[] { Bundle.class });
- methodonCreate.setAccessible(true);
- methodonCreate.invoke(instance, new Object[] { paramBundle });
- }
- return;
- } catch (Exception ex) {
- ex.printStackTrace();
- }
- }
- }
從上面可以看到,由於我們不能像系統一樣初始化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程式碼- <category android:name="android.intent.category.XXXX" />
還後,在門戶的專案裡,對當前手機進行探測,看有多少個含有這個類別的Activity,可以進行顯示出來了。
結論:
對了我們這種應用app的程式,用這種方式,會大大加大開發的複雜度,不過其第二種方法不錯,但要變化著使用。
使用交叉推薦:
1. 在每個不同的專案裡,都加入對其他產品的推薦(同時,檢測使用者手機裡已經安裝過的apk)。
2. 有一些功能,還可以讓使用者跳到其他的apk實現。