通过分析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实现。