探秘腾讯Android手机游戏平台之不安装游戏APK直接启动法

探秘腾讯Android手机游戏平台之不安装游戏APK直接启动法
2011年09月18日  ⁄ 移动开发  ⁄ 评论数 38 ⁄ 被围观 8,791+

前言

相信这样一个问题,大家都不会陌生,

“有什么的方法可以使Android的程序APK不用安装,而能够直接启动”。

发现最后的结局都是不能实现这个美好的愿望,而腾讯Android手机游戏平台却又能实现这个功能,下载的连连看,五子棋都没有安装过程,但是都能直接运行,这其中到底有什么“玄机”呢,也有热心童鞋问过我这个问题,本文就为大家来揭开这个谜团。

重要说明

在实践的过程中大家都会发现资源引用的问题,这里重点声明两点:
1. 资源文件是不能直接inflate的,如果简单的话直接在程序中用代码书写。
2. 资源文件是不能用R来引用的,因为上下文已经不同了,腾讯的做法是将资源文件打包(*.pak文件和APK打包在一起),虽然APK是没有进行安装,但是资源文件是另外解压到指定文件夹下面的,然后将文件夹的地址传给了第三方应用程序,这样第三方应用程序通过File的inputstream流还是可以读取和使用这些资源的。

实践

我实现了一个小小的Demo,麻雀虽小五脏俱全,为了突出原理,我就尽量简化了程序,通过这个实例来让大家明白后台的工作原理。

  1. 下载demo的apk程序apks,其中包括了两个apk,分别是A和B
  2. 这两个APK可分别安装和运行,A程序界面只显示一个Button,B程序界面会动态显示当前的时间
  3. 下面的三幅图片分别为直接启动运行A程序(安装TestA.apk),直接启动运行B程序(安装TestB.apk)和由A程序动态启动B程序(安装TestA.apk,TestB.apk不用安装,而是放在/mnt/sdcard/目录中,即 SD卡上)的截图,细心的同学可以停下来观察一下他们之间的不同
  4. 后两幅图片的不同,也即Title的不同,则解释出了我们将要分析的后台实现原理的机制

实现原理

最能讲明白道理的莫过于源码了,下面我们就来分析一下A和B的实现机制,首先来分析TestA.apk的主要代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    @Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);Button btn = (Button) findViewById(R.id.btn);btn.setOnClickListener(new OnClickListener() {@Overridepublic 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);}});}

代码解析:这就是OnCreate函数要做的事情,装载view界面,绑定button事件,大家都熟悉了,还有就是设置程序B的放置路径,因为我程序中代码是从/mnt/sdcard/TestB.apk中动态加载,这也就是为什么要让大家把TestB.apk放在SD卡上面的原因了。关键的函数就是最后一个了LoadAPK,它来实现动态加载B程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
    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);Constructor localConstructor = localClass.getConstructor(new Class[] {});Object 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();}}

代码解析:这个函数要做的工作如下:加载B程序的APK文件,通过类加载器DexClassLoader来解析APK文件,这样会在SD卡上面生成一个同名的后缀为dex的文件,例如/mnt/sdcard/TestB.apk==>/mnt/sdcard/TestB.dex,接下来就是通过java反射机制,动态实例化B中的Activity对象,并依次调用了其中的两个函数,分别为setActivity和onCreate.看到这里,大家是不是觉得有点奇怪,Activity的启动函数是onCreate,为什么要先调用setActivity,而更奇怪的是setActivity并不是系统的函数,确实,那是我们自定义的,这也就是核心的地方。

好了带着这些疑问,我们再来分析B程序的主代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class TestBActivity extends Activity {private static final String TAG = "TestBActivity";private Activity otherActivity;@Overridepublic 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(R.layout.main);setContentView(new TBSurfaceView(this));}}public void setActivity(Activity paramActivity) {Log.d(TAG, "setActivity..." + paramActivity);this.otherActivity = paramActivity;}
}

代码解析:看完程序B的实现机制,大家是不是有种恍然大悟的感觉,这根本就是“偷梁换柱”嘛,是滴,程序B动态借用了程序A的上下文执行环境,这也就是上面后两幅图的差异,最后一幅图运行的是B的程序,但是title表示的却是A的信息,而没有重新初始化自己的,实际上这也是不可能的,所以有些童鞋虽然通过java的反射机制,正确呼叫了被调程序的onCreate函数,但是期望的结果还是没有出现,原因就是这个上下文环境没有正确建立起来,但是若通过startActivity的方式来启动APK的话,android系统会替你建立正确的执行时环境,所以就没问题。至于那个TBSurfaceView,那就是自定义的一个view画面,动态画当前的时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public class TBSurfaceView extends SurfaceView implements Callback, Runnable {private SurfaceHolder sfh;private Thread th;private Canvas canvas;private Paint paint;public TBSurfaceView(Context context) {super(context);th = new Thread(this);sfh = this.getHolder();sfh.addCallback(this);paint = new Paint();paint.setAntiAlias(true);paint.setColor(Color.RED);this.setKeepScreenOn(true);}public void surfaceCreated(SurfaceHolder holder) {th.start();}private void draw() {try {canvas = sfh.lockCanvas();if (canvas != null) {canvas.drawColor(Color.WHITE);canvas.drawText("Time: " + System.currentTimeMillis(), 100,100, paint);}} catch (Exception ex) {ex.printStackTrace();} finally {if (canvas != null) {sfh.unlockCanvasAndPost(canvas);}}}public void run() {while (true) {draw();try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {}public void surfaceDestroyed(SurfaceHolder holder) {}
}

腾讯游戏平台解析

说了这么多,都是背景,O(∩_∩)O哈哈~

其实腾讯游戏平台就是这么个实现原理,我也是通过它才学习到这种方式的,还得好好感谢感谢呢。

腾讯Android游戏平台的游戏分成两类,第一类是腾讯自主研发的,像斗地主,五子棋,连连看什么的,所以实现机制就如上面的所示,A代表游戏大厅,B代表斗地主类的小游戏。第二类是第三方软件公司开发的,可就不能已这种方式来运作了,毕竟腾讯不能限制别人开发代码的方式啊,所以腾讯就开放了一个sdk包出来,让第三方应用可以和游戏大厅相结合,具体可参见QQ游戏中心开发者平台,但这同时就损失了一个优点,那就是第三方开发的游戏要通过安装的方式才能运行。

结论

看到这里,相信大家都比较熟悉这个背后的原理了吧,也希望大家能提供更好的反馈信息!

程序源码下载source

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/1621929.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

群玩小游戏sdk一键接入

群玩提供第三方用户互动内容服务,拥有“你画我猜”、“谁是卧底”、“豆腐王国”、“猜歌名” 等多款互动竞猜类游戏、智能机器人接入服务,适合直播、社交、社群行业,可以自定义设计玩法,页面精美有趣,接入方便快捷&am…

职业成长面对面交流会

职业成长面对面交流会 ——微软高级主管欧琼老师讲座2009年4月12日星期日上午十点半由电子工业出版社博文视点资讯有限公司隆重举办一场“职业成长面对面交流会”,主讲老师是《微软360度-成功和成长》主要作者 微软总部高级主管 欧琼老师,精彩的讲座在开…

游戏客户端面试(Unity)

推荐阅读: 我的CSDN 我的博客园 QQ群:704621321 我的个人博客 一。最开始的两家公司笔试面试题目 一家公司是学校聘请研究教育方面VR课件的公司,面试没几天,就收到了面试通过的消息,后面因为通过了另一家游戏公司而拒绝…

Unity - 微信小游戏

总参考:Unity WebGL 微信小游戏适配方案(公测) 安装与使用 下载 Unity插件,并导入至游戏项目中,版本更新请查看更新日志 请查阅推荐引擎版本,安装时选择WebGL组件 最终选择Unity2021.2.5f1c1InstantGame前往Node官网安装长期稳定…

聊天群内组局互动小游戏

“找我61”互动小游戏嵌入到聊天群内,会给社交应用(产品)增加不少人气,带来更多活力。 新颖社区社交产品“知多视广”架构构思设想

活动预告 | 与大神面对面,移动游戏开发者必须掌握的十大技巧

上半年,国内游戏下载量对比2019年下半年上涨将近50%,在用户旺盛的需求下,游戏行业迎来了绝佳的发展机遇。在市场火爆的背后,随着用户需求的多样化、时间的碎片化,一些因开发繁琐、资源不足的问题频频出现,让…

游戏大厅 从基础开始(1)——最简单的关系,用户与房间

游戏大厅 从基础开始(1) ——最简单的关系,用户与房间。 做游戏 就好像写一篇简单的记叙文 作为小学语文大纲要求 记叙文需要完整地叙述: 时间,地点,人物,事件 也就是 WHEN , WH…

RK3399平台开发系列讲解(存储篇)Linux 存储系统的 I/O 栈

平台内核版本安卓版本RK3399Linux4.4Android7.1🚀返回专栏总目录 文章目录 一、Linux 存储系统全景二、Linux 存储系统的缓存沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇将介绍 Linux 存储系统的 I/O 原理。 一、Linux 存储系统全景 我们可以把 Linux 存储系…

解决MASM32代码汇编出错: error A2181: initializer must be a string or single item

最近用MASM32编程更新SysInfo,增加对IPv6连接信息的收集,使用到了 typedef struct _MIB_TCP6ROW_OWNER_MODULE {UCHAR ucLocalAddr[16];DWORD dwLocalScopeId;DWORD dwLocalPort;UCHAR ucRemoteAddr[16];DWORD …

文本分类任务

文章目录 引言1. 文本分类-使用场景2. 自定义类别任务3. 贝叶斯算法3.1 预备知识3.2 贝叶斯公式3.3 贝叶斯公式的应用3.4 贝叶斯公式在NLP中的应用3.5 贝叶斯公式-文本分类3.6 代码实现3.7 贝叶斯算法的优缺点 4. 支持向量机4.1 支持向量机-核函数4.2 支持向量机-解决多分类4.3…

android系统启动流程之zygote如何创建SystemServer进程

SystemServer:是独立的进程,主要工作是管理服务的,它将启动大约90种服务Services. 它主要承担的职责是为APP的运行提供各种服务,像AMS,WMS这些服务并不是一个独立的进程, 它们其实都是SystemServer进程中需要管理的的众多服务之一…

从零到富:探索CSGO搬砖项目的无限可能

在如今互联网时代,有一项令人惊叹的项目正悄然兴起,它就是CSGO搬砖项目。作为一个从零开始的家伙,我亲身经历了这个项目的神奇魅力,每天轻松赚取几十上百的收益,无风险,低成本。今天,我将带领大…

Redis—Redis介绍(是什么/为什么快/为什么做MySQL缓存等)

一、Redis是什么 Redis 是一种基于内存的数据库,对数据的读写操作都是在内存中完成,因此读写速度非常快,常用于缓存,消息队列、分布式锁等场景。 Redis 提供了多种数据类型来支持不同的业务场景,比如 String(字符串)、…

围棋与育种

最近因为娃子报名了围棋课,我本着交一次课学两个人的态度,入门围棋,买了些书,听了些课,有所得。学了两个多月,现在6级水平,了解了基本的攻杀技巧,会判断输赢。 下面截图是今天的一盘…

QQ号大规模被盗与你我有什么关系?你我该如何做?

QQ号大规模被盗与你我有什么关系?你我该如何做? 时值QQ号大规模被告事件已经过去多天,今天,我们再次回顾一下此次事件的经过: 1、大规模用户被盗号。 2、不法分子用盗得账号向家人、朋友及其他联系人发送不雅图片。 …

wireshark抓包QQ号

过滤栏输入oicq,然后随便打开一个数据包,就可看到其qq号。 查看移动端QQ的账号 因为是用的是TCP协议,而TCP数据包众多,所以不方便过滤。 但是我发现在info栏中带有scotty-ft字样的数据包中可以找到qq号。你可以按ctrlF选择字符串过…

QQ号1941663132是一个诈骗

我一个拼多多订单(20元)中通运单丢失; 然后两个自称物流公司的电话联系我:19141980127/18988189942; 说是赔偿我 100 多块; 叫我订单截图发给他们客服QQ1941663132; 加了QQ、截图隐私信息打马赛克发了他; 对方给我一个二维码,我解析了一下,是一个 HTTP 协议不安全的网址…

[笔记] 阿里云域名知识

文章目录 前言一、域名二、域名常见分类2.1 泛域名2.2 为什么要设置子域名 三、记录类型3.1 A- 将域名指向一个PV4地址3.2 CNAME- 将域名指向另外一个域名3.3 AAAA- 将域名指向一个PV6地址3.4 MX- 将域名指向邮件服务器地址3.5 SRV- 记录提供特定的服务的服务器使用场景 3.6 TX…

飞秋接收文件提示防火墙

1、效果: 2、打开控制面板,设置小图标模式 3、打开防火墙设置 4、入站规则:把飞秋的路径添加 5、同理把出站规则也配一下。

飞秋与虚拟机的有关IP设置

由于各种原因公司规定,有一部分机器是不能上外网的。所以统一使用飞秋,这个聊天软件在公司内部。 今天,在自己的机器上弄了一个虚拟机,呵呵,你懂的哦,肯定使用的VMware了,不过咱使用的比较高的版…