先抛砖引玉一下,接个例子说明,现在的例子是要实现:
一个安装的apk里面的一个activity A要去调用一个放在sd卡上的没安装的apk里面的activity B,点返回键又回到Activity A,而不是直接退出。
ok,我这里已经有现成的方法了,先上代码:
主动去加载的SD上类的activity
import java.lang.reflect.Constructor; import java.lang.reflect.Method; import android.app.Activity; import android.content.Intent; import android.content.pm.PackageInfo; import android.os.Bundle; import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.Toast; import dalvik.system.DexClassLoader; public class MainActivity extends Activity { private static final String TAG = "MainActivity"; private boolean isJumped = false; private Class localClass; private Object instance; @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) { isJumped = true; Bundle paramBundle = new Bundle(); paramBundle.putBoolean("KEY_START_FROM_OTHER_ACTIVITY", true); String dexpath = "/mnt/sdcard/ToolA.apk"; String dexoutputpath = "/mnt/sdcard/"; LoadAPK(paramBundle, dexpath, dexoutputpath); } }); } @Override protected void onDestroy() { super.onDestroy(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { if (isJumped) { Bundle paramBundle = new Bundle(); paramBundle.putBoolean("KEY_START_FROM_OTHER_ACTIVITY", true); closeAPK(paramBundle); isJumped = false; return true; } } return super.onKeyDown(keyCode, event); } 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); localClass = localDexClassLoader.loadClass(activityname); Constructor localConstructor = localClass.getConstructor(new Class[] {}); instance = localConstructor.newInstance(new Object[] {}); Log.d(TAG, "instance = " + 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(); } } public void closeAPK(Bundle paramBundle) { try { Method methodonCreate = localClass.getDeclaredMethod("onGoBack"); methodonCreate.setAccessible(true); methodonCreate.invoke(instance); } catch (Exception ex) { ex.printStackTrace(); } } }
package mobi.thinkchange.android.toola; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.KeyEvent; import android.widget.Toast; public class ToolAActivity extends Activity { private static final String TAG = "ToolAActivity"; private Activity otherActivity; @Override public void onCreate(Bundle savedInstanceState) { boolean b = false; if (savedInstanceState != null) { b = savedInstanceState.getBoolean("KEY_START_FROM_OTHER_ACTIVITY", false); if (b) { this.otherActivity.setContentView(new TBSurfaceView(this.otherActivity)); } } if (!b) { super.onCreate(savedInstanceState); setContentView(new TBSurfaceView(this)); } } public void setActivity(Activity paramActivity) { Log.d(TAG, "setActivity..." + paramActivity); this.otherActivity = paramActivity; } public void onGoBack() { otherActivity.setContentView(R.layout.main); } }
(代码只是片段,如果要实际应用还需要完善,比如在返回的时候终端执行的runnable可参考我的另外一篇文章如何终端执行的java线程)
也许网上有很多关于QQ游戏大厅的分析或者类似上面的例子,但是我想做进一步的分析。
1.我们知道一个activity具有严格意义上的生命周期必须要在ActivityThread里面去创建,所以通过DexClassLoader加载的ActivityB是没有生命周期的,它也就是一个普通的类;
2.有人会问,为什么还要让ActivityA 来加载ActivityB,而不是直接ActivityA去load 另外一个类比如classC呢? 这个问题很简单,因为我们在打包成APK的时候,有activity会更方便和直观(自己去体会)。简单的游戏,往往一个activity就够了。那么ActivityA 也仅仅只需要加载一个类实例。