日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

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

發(fā)布時(shí)間:2025/7/14 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 探秘腾讯Android手机游戏平台之不安装游戏APK直接启动法 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

相信這樣一個(gè)問題,大家都不會陌生,

“有什么的方法可以使Android的程序APK不用安裝,而能夠直接啟動”。

發(fā)現(xiàn)最后的結(jié)局都是不能實(shí)現(xiàn)這個(gè)美好的愿望,而騰訊Android手機(jī)游戲平臺卻又能實(shí)現(xiàn)這個(gè)功能,下載的連連看,五子棋都沒有安裝過程,但是都能直接運(yùn)行,這其中到底有什么“玄機(jī)”呢,也有熱心童鞋問過我這個(gè)問題,本文就為大家來揭開這個(gè)謎團(tuán)。

重要說明

在實(shí)踐的過程中大家都會發(fā)現(xiàn)資源引用的問題,這里重點(diǎn)聲明兩點(diǎn): 1. 資源文件是不能直接inflate的,如果簡單的話直接在程序中用代碼書寫。 2. 資源文件是不能用R來引用的,因?yàn)樯舷挛囊呀?jīng)不同了,騰訊的做法是將資源文件打包(*.pak文件和APK打包在一起),雖然APK是沒有進(jìn)行安裝,但是資源文件是另外解壓到指定文件夾下面的,然后將文件夾的地址傳給了第三方應(yīng)用程序,這樣第三方應(yīng)用程序通過File的inputstream流還是可以讀取和使用這些資源的。

實(shí)踐

我實(shí)現(xiàn)了一個(gè)小小的Demo,麻雀雖小五臟俱全,為了突出原理,我就盡量簡化了程序,通過這個(gè)實(shí)例來讓大家明白后臺的工作原理。

  • 下載demo的apk程序apks,其中包括了兩個(gè)apk,分別是A和B
  • 這兩個(gè)APK可分別安裝和運(yùn)行,A程序界面只顯示一個(gè)Button,B程序界面會動態(tài)顯示當(dāng)前的時(shí)間
  • 下面的三幅圖片分別為直接啟動運(yùn)行A程序(安裝TestA.apk),直接啟動運(yùn)行B程序(安裝TestB.apk)和由A程序動態(tài)啟動B程序(安裝TestA.apk,TestB.apk不用安裝,而是放在/mnt/sdcard/目錄中,即 SD卡上)的截圖,細(xì)心的同學(xué)可以停下來觀察一下他們之間的不同
  • 后兩幅圖片的不同,也即Title的不同,則解釋出了我們將要分析的后臺實(shí)現(xiàn)原理的機(jī)制
  • 實(shí)現(xiàn)原理

    最能講明白道理的莫過于源碼了,下面我們就來分析一下A和B的實(shí)現(xiàn)機(jī)制,首先來分析TestA.apk的主要代碼實(shí)現(xiàn):

    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() { ? ? ? ? ? ? ? @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); ? ? ? ? ? ? } ? ? ? ? }); ? ? }

    代碼解析:這就是OnCreate函數(shù)要做的事情,裝載view界面,綁定button事件,大家都熟悉了,還有就是設(shè)置程序B的放置路徑,因?yàn)槲页绦蛑写a是從/mnt/sdcard/TestB.apk中動態(tài)加載,這也就是為什么要讓大家把TestB.apk放在SD卡上面的原因了。關(guān)鍵的函數(shù)就是最后一個(gè)了LoadAPK,它來實(shí)現(xiàn)動態(tài)加載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(); ? ? ? ? } ? ? }

    代碼解析:這個(gè)函數(shù)要做的工作如下:加載B程序的APK文件,通過類加載器DexClassLoader來解析APK文件,這樣會在SD卡上面生成一個(gè)同名的后綴為dex的文件,例如/mnt/sdcard/TestB.apk==>/mnt/sdcard/TestB.dex,接下來就是通過java反射機(jī)制,動態(tài)實(shí)例化B中的Activity對象,并依次調(diào)用了其中的兩個(gè)函數(shù),分別為setActivity和onCreate.看到這里,大家是不是覺得有點(diǎn)奇怪,Activity的啟動函數(shù)是onCreate,為什么要先調(diào)用setActivity,而更奇怪的是setActivity并不是系統(tǒng)的函數(shù),確實(shí),那是我們自定義的,這也就是核心的地方。

    好了帶著這些疑問,我們再來分析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; ? ? ? @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(R.layout.main); ? ? ? ? ? ? setContentView(new TBSurfaceView(this)); ? ? ? ? } ? ? } ? ? ? public void setActivity(Activity paramActivity) { ? ? ? ? Log.d(TAG, "setActivity..." + paramActivity); ? ? ? ? this.otherActivity = paramActivity; ? ? } }

    代碼解析:看完程序B的實(shí)現(xiàn)機(jī)制,大家是不是有種恍然大悟的感覺,這根本就是“偷梁換柱”嘛,是滴,程序B動態(tài)借用了程序A的上下文執(zhí)行環(huán)境,這也就是上面后兩幅圖的差異,最后一幅圖運(yùn)行的是B的程序,但是title表示的卻是A的信息,而沒有重新初始化自己的,實(shí)際上這也是不可能的,所以有些童鞋雖然通過java的反射機(jī)制,正確呼叫了被調(diào)程序的onCreate函數(shù),但是期望的結(jié)果還是沒有出現(xiàn),原因就是這個(gè)上下文環(huán)境沒有正確建立起來,但是若通過startActivity的方式來啟動APK的話,android系統(tǒng)會替你建立正確的執(zhí)行時(shí)環(huán)境,所以就沒問題。至于那個(gè)TBSurfaceView,那就是自定義的一個(gè)view畫面,動態(tài)畫當(dāng)前的時(shí)間

    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哈哈~

    其實(shí)騰訊游戲平臺就是這么個(gè)實(shí)現(xiàn)原理,我也是通過它才學(xué)習(xí)到這種方式的,還得好好感謝感謝呢。

    騰訊Android游戲平臺的游戲分成兩類,第一類是騰訊自主研發(fā)的,像斗地主,五子棋,連連看什么的,所以實(shí)現(xiàn)機(jī)制就如上面的所示,A代表游戲大廳,B代表斗地主類的小游戲。第二類是第三方軟件公司開發(fā)的,可就不能已這種方式來運(yùn)作了,畢竟騰訊不能限制別人開發(fā)代碼的方式啊,所以騰訊就開放了一個(gè)sdk包出來,讓第三方應(yīng)用可以和游戲大廳相結(jié)合,具體可參見QQ游戲中心開發(fā)者平臺,但這同時(shí)就損失了一個(gè)優(yōu)點(diǎn),那就是第三方開發(fā)的游戲要通過安裝的方式才能運(yùn)行。

    結(jié)論

    看到這里,相信大家都比較熟悉這個(gè)背后的原理了吧,也希望大家能提供更好的反饋信息!

    程序源碼下載source

    轉(zhuǎn)載于:https://www.cnblogs.com/xuweili/articles/4023265.html

    總結(jié)

    以上是生活随笔為你收集整理的探秘腾讯Android手机游戏平台之不安装游戏APK直接启动法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。