不安装游戏apk直接启动法
原文地址:http://blog.zhourunsheng.com/2011/09/%E6%8E%A2%E7%A7%98%E8%85%BE%E8%AE%AFandroid%E6%89%8B%E6%9C%BA%E6%B8%B8%E6%88%8F%E5%B9%B3%E5%8F%B0%E4%B9%8B%E4%B8%8D%E5%AE%89%E8%A3%85%E6%B8%B8%E6%88%8Fapk%E7%9B%B4%E6%8E%A5%E5%90%AF%E5%8A%A8%E6%B3%95/?utm_source=rss&utm_medium=rss&utm_campaign=%25e6%258e%25a2%25e7%25a7%2598%25e8%2585%25be%25e8%25ae%25afandroid%25e6%2589%258b%25e6%259c%25ba%25e6%25b8%25b8%25e6%2588%258f%25e5%25b9%25b3%25e5%258f%25b0%25e4%25b9%258b%25e4%25b8%258d%25e5%25ae%2589%25e8%25a3%2585%25e6%25b8%25b8%25e6%2588%258fapk%25e7%259b%25b4%25e6%258e%25a5%25e5%2590%25af%25e5%258a%25a8%25e6%25b3%2595
前言
相信這樣一個問題,大家都不會陌生,
“有什么的方法可以使Android的程序APK不用安裝,而能夠直接啟動”。
發現最后的結局都是不能實現這個美好的愿望,而騰訊Android手機游戲平臺卻又能實現這個功能,下載的連連看,五子棋都沒有安裝過程,但是都能直接運行,這其中到底有什么“玄機”呢,也有熱心童鞋問過我這個問題,本文就為大家來揭開這個謎團。
重要說明
在實踐的過程中大家都會發現資源引用的問題,這里重點聲明兩點:
1. 資源文件是不能直接inflate的,如果簡單的話直接在程序中用代碼書寫。
2. 資源文件是不能用R來引用的,因為上下文已經不同了,騰訊的做法是將資源文件打包(*.pak文件和APK打包在一起),雖然APK是沒有進行安裝,但是資源文件是另外解壓到指定文件夾下面的,然后將文件夾的地址傳給了第三方應用程序,這樣第三方應用程序通過File的inputstream流還是可以讀取和使用這些資源的。
實踐
我實現了一個小小的Demo,麻雀雖小五臟俱全,為了突出原理,我就盡量簡化了程序,通過這個實例來讓大家明白后臺的工作原理。
實現原理
最能講明白道理的莫過于源碼了,下面我們就來分析一下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
轉載于:https://www.cnblogs.com/WIT-Evan/archive/2013/05/17/7291434.html
總結
以上是生活随笔為你收集整理的不安装游戏apk直接启动法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 记录一次文件迁移
- 下一篇: Oracle9i OCM认证实践课考试的