`
handsomeliuyang
  • 浏览: 311891 次
  • 性别: Icon_minigender_1
  • 来自: 益阳
社区版块
存档分类
最新评论

浅析QQGame

阅读更多

通过分析QQGame的项目,发现其存在两种方式:

1. 不安装游戏apk,直接启动

我这里只说其原理,详情讲查看:探秘腾讯Android手机游戏平台之不安装游戏APK直接启动法

其原理是:

  1. 把apk里的class文件通过DexClassLoader把apk里的class文件全部加载到java虚拟机里,如果要使用其中的某个class时,就要使用反射来调用。

  2. 如果这个类是Activity的子类,那如何来启动,Activity的子类是由android系统来创建,处理方法是:把Activity的子类当做一个有着Activity相应接口的类,在项目里创建一个空的Activity类,其里面不做任何事情,只是用反射调用真正的Activity的方法,代码如下:

 

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", true);
				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:

 

<category android:name="android.intent.category.XXXX" />

还后,在门户的项目里,对当前手机进行探测,看有多少个含有这个类别的Activity,可以进行显示出来了。

 

结论:

对了我们这种应用app的程序,用这种方式,会大大加大开发的复杂度,不过其第二种方法不错,但要变化着使用。

使用交叉推荐:

  1. 在每个不同的项目里,都加入对其他产品的推荐(同时,检测用户手机里已经安装过的apk)。

  2. 有一些功能,还可以让用户跳到其他的apk实现。

 

 

分享到:
评论
2 楼 hgzhss 2012-07-23  
Method localMethodSetActivity = localClass.getDeclaredMethod( 
                        "setActivity", new Class[] { Activity.class }); 
                localMethodSetActivity.setAccessible(true); 
                localMethodSetActivity.invoke(instance, new Object[] { this });

中的 setActivity 这方法 里面是什么内容呀 ,为什么我用不了这个东西,他不是Activity 的方法,应该是个自定义的方法,他是怎么实现的呀

如果这个是个自定义的方法 ,那他就不能任意的打开apk的 Activity
1 楼 down_data01 2012-03-15  
奇思妙想哦

相关推荐

Global site tag (gtag.js) - Google Analytics