关于QQ游戏大厅的架构我也想说几句

先抛砖引玉一下,接个例子说明,现在的例子是要实现:

一个安装的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();
		}
	}
}

另外一个是sd卡上待加载的activity:

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);
    }
}

上面是实现我说的例子最核心的两个类,全部代码详见:http://download.csdn.net/detail/androidzhaoxiaogang/4903804

(代码只是片段,如果要实际应用还需要完善,比如在返回的时候终端执行的runnable可参考我的另外一篇文章如何终端执行的java线程)

也许网上有很多关于QQ游戏大厅的分析或者类似上面的例子,但是我想做进一步的分析。

1.我们知道一个activity具有严格意义上的生命周期必须要在ActivityThread里面去创建,所以通过DexClassLoader加载的ActivityB是没有生命周期的,它也就是一个普通的类;

2.有人会问,为什么还要让ActivityA 来加载ActivityB,而不是直接ActivityA去load 另外一个类比如classC呢? 这个问题很简单,因为我们在打包成APK的时候,有activity会更方便和直观(自己去体会)。简单的游戏,往往一个activity就够了。那么ActivityA 也仅仅只需要加载一个类实例。


你可能感兴趣的:(关于QQ游戏大厅的架构我也想说几句)