DVM執(zhí)行 java 程序的工具

  1. jvm 執(zhí)行字節(jié)碼原理:java 程序運(yùn)行時(shí),是由一個(gè) java 虛擬機(jī)來解釋 java 字節(jié)碼的,它將這些字節(jié)碼翻譯成本地 CPU 的指令碼代乃,然后執(zhí)行。
  2. Android 執(zhí)行指令碼原理:Android 應(yīng)用程序打包成 dex 包后仿粹,通過系統(tǒng)程序 dalvikvm 創(chuàng)建一個(gè)虛擬機(jī)來執(zhí)行參數(shù)中指定的 java 類搁吓。相對(duì)于 java 而言,負(fù)責(zé)解釋并執(zhí)行的就是一個(gè)虛擬機(jī)吭历,而對(duì)于 Linux 而言堕仔,這就是一個(gè)普通的進(jìn)程,它與一個(gè)只有一行代碼的 Hello World 的可執(zhí)行程序無本質(zhì)區(qū)別晌区。
  3. Android 啟動(dòng)一個(gè)虛擬機(jī)的方法跟啟動(dòng)任何一個(gè)可執(zhí)行程序的方法是相同的摩骨,在命令行下輸入可執(zhí)行程序的名稱,并在參數(shù)中指定要執(zhí)行的 java 類即可。

dalvikvm

dalvikvm 作用:創(chuàng)建一個(gè)虛擬機(jī)并執(zhí)行指定的 java 類

dalvikvm 命令:dalvikvm -cp 文件路徑 權(quán)限類名
如:
dalvikvm -cp /data/app/Demo.dex Demo

實(shí)例演示:

  • JVM 執(zhí)行 java 程序的過程:

    1. 編譯成二進(jìn)制文件:javac Demo.java
    2. 翻譯成機(jī)器碼并執(zhí)行:java Demo
        /**
         * 2019-05-18
         * java code for simple Demo
         */
    public class Demo {
        public static void main(String[] args) {
            System.out.println("Demo:: Hello Android");
        }
    }
    
  • DVM 執(zhí)行 java 程序過程:對(duì)于 Android 而言,可執(zhí)行代碼需要轉(zhuǎn)化成可執(zhí)行的 dex 優(yōu)化文件才能被系統(tǒng)加載執(zhí)行径簿。核心思想是將字節(jié)碼文件轉(zhuǎn) dex 后,由 dvm 翻譯執(zhí)行灾馒。

    1. 打包為 jar 包: jar cvf Demo.jar Demo.class
    2. jar 轉(zhuǎn) dex:dx --dex --output= Demo1.jar Demo.jar
    3. 開一個(gè) Android 模擬器,使用 Android 原生或 Genermotion 模擬器即可遣总。
    4. 掛載設(shè)備:adb root;adb remount
    5. 安裝程序:adb push Demo1.jar /data/app/Demo.dex
    6. 執(zhí)行程序:adb shell dalvikvm -cp /data/app/Demo.dex Demo
  • DVM 執(zhí)行 java 程序的過程中可能遇到的錯(cuò)誤:

    • java sdk 和 dx 工具的要求版本不一致時(shí)你虹,解決這種轉(zhuǎn)換問題一般發(fā)生在第二步,比如我本地的 java 版本是 1.8.0_101-b13彤避,可以使用 27.0.0 中的 build-tools 下的 dx 工具。
        PARSE ERROR:
        unsupported class file version 52.0
        ...while parsing Demo.class
        1 error; aborting
    
    • push 了非 dex 優(yōu)化文件到系統(tǒng)夯辖,執(zhí)行 dalvikvm 命令琉预,出現(xiàn)如下錯(cuò)誤:
        Unable to locate class 'Demo' java.lang.ClassNotFoundException: Didn't find class "Demo" on path: DexPathList[[zip file "/data/app/Demo.jar"],nativeLibraryDirectories=[/system/lib, /vendor/lib, /system/lib, /vendor/lib]]
    at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:380)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
    Suppressed: java.io.IOException: No original dex files found for dex location /data/app/Demo.jar
        at dalvik.system.DexFile.openDexFileNative(Native Method)
        at dalvik.system.DexFile.openDexFile(DexFile.java:367)
        at dalvik.system.DexFile.<init>(DexFile.java:112)
        at dalvik.system.DexFile.<init>(DexFile.java:77)
        at dalvik.system.DexPathList.loadDexFile(DexPathList.java:359)
        at dalvik.system.DexPathList.makeElements(DexPathList.java:323)
        at dalvik.system.DexPathList.makeDexElements(DexPathList.java:263)
        at dalvik.system.DexPathList.<init>(DexPathList.java:126)
        at dalvik.system.BaseDexClassLoader.<init>(BaseDexClassLoader.java:48)
        at dalvik.system.PathClassLoader.<init>(PathClassLoader.java:64)
        at java.lang.ClassLoader.createSystemClassLoader(ClassLoader.java:224)
        at java.lang.ClassLoader.-wrap0(ClassLoader.java)
        at java.lang.ClassLoader$SystemClassLoader.<clinit>(ClassLoader.java:183)
        at java.lang.ClassLoader.getSystemClassLoader(ClassLoader.java:1097)
        Exception in thread "main" java.lang.ClassNotFoundException: Didn't find class "Demo" on path: DexPathList[[zip file "/data/app/Demo.jar"],nativeLibraryDirectories=[/system/lib, /vendor/lib, /system/lib, /vendor/lib]]
    at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:380)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
    Suppressed: java.io.IOException: No original dex files found for dex location /data/app/Demo.jar
        at dalvik.system.DexFile.openDexFileNative(Native Method)
        at dalvik.system.DexFile.openDexFile(DexFile.java:367)
        at dalvik.system.DexFile.<init>(DexFile.java:112)
        at dalvik.system.DexFile.<init>(DexFile.java:77)
        at dalvik.system.DexPathList.loadDexFile(DexPathList.java:359)
        at dalvik.system.DexPathList.makeElements(DexPathList.java:323)
        at dalvik.system.DexPathList.makeDexElements(DexPathList.java:263)
        at dalvik.system.DexPathList.<init>(DexPathList.java:126)
        at dalvik.system.BaseDexClassLoader.<init>(BaseDexClassLoader.java:48)
        at dalvik.system.PathClassLoader.<init>(PathClassLoader.java:64)
        at java.lang.ClassLoader.createSystemClassLoader(ClassLoader.java:224)
        at java.lang.ClassLoader.-wrap0(ClassLoader.java)
        at java.lang.ClassLoader$SystemClassLoader.<clinit>(ClassLoader.java:183)
        at java.lang.ClassLoader.getSystemClassLoader(ClassLoader.java:1097)
    
    
  • 這種錯(cuò)誤很常見,和之前應(yīng)用程序加載類蒿褂,加載函數(shù)的錯(cuò)誤類似圆米,這種錯(cuò)誤如果在 APK 程序內(nèi)發(fā)生卒暂。
    出現(xiàn)這種情況,一般是應(yīng)用程序未簽名娄帖,引用的函數(shù)在系統(tǒng)中并不存在也祠。而在 java 程序內(nèi),則是 java 程序不正確導(dǎo)致近速,需要確認(rèn)此 jar 是否經(jīng)過 dex 轉(zhuǎn)化或當(dāng)前虛擬機(jī)版本要求的系統(tǒng)優(yōu)化诈嘿。


dvz

dvz 作用:從 zygote 進(jìn)程中孵化出一個(gè)新的進(jìn)程,新的進(jìn)程也是一個(gè) Dalvik 虛擬機(jī)削葱。該進(jìn)程與 dalvikvm 啟動(dòng)的虛擬機(jī)相比奖亚,區(qū)別在于該進(jìn)程中已經(jīng)預(yù)裝了 Framework 中的大部分類和資源。

dvz 命令:dvz -classpath 文件路徑 權(quán)限類名
如:
dvz -classpath /system/app/Demo.apk com.my.demo.DemoActivity

關(guān)于 Demo.apk析砸,其代碼如下:

package com.my.demo;

import android.app.*;

/**
 * 2019-05-18
 * java code for dvz Demo
 */
public class DemoActivity extends Activity {
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo);
    }
    
    public static void main(String[] args) {
        System.out.println("DemoActivity::Hello Android:: DemoActivity");
    }
}

上面的 main 函數(shù)并不是該程序的入口昔字,只是用來作為開發(fā)調(diào)試。app 的主入口在 ActivityThread 中首繁。

遺憾的是在系統(tǒng)中暫無 dvz 工具作郭,暫時(shí)也未找到 dvz 的相關(guān)資料


app_process

app_process 作用:Framework 啟動(dòng)過程中,加載 ZygoteInit.java 和 SystemServer.java弦疮,也可以用來調(diào)試 java 程序

app_process 命令:app_process -Djava.class.path=文件路徑 路徑 權(quán)限類名

如:app_process -Djava.class.path=/data/app/Demo.dex /data/app Demo

  • app_process 執(zhí)行 java 程序的過程:

    1. 創(chuàng)建 Demo.java
    2. 執(zhí)行 javac Demo.java
    3. 打包 jar 文件:java cvf Demo.jar Demo.class
    4. jar 轉(zhuǎn) dex:dx --dex --output=Demo1.jar Demo.jar
    5. 掛載設(shè)備:adb root;adb remount
    6. 將文件 push 到模擬器:adb push Demo1.jar /data/app/Demo.dex
    7. 執(zhí)行命令:adb shell app_process -Djava.class.path=/data/app/Demo.dex /data/app Demo
  • app_process 命令參數(shù):app_process [java-options] cmd-dir start-class-name [options]


由 app_process 想到的夹攒?

  • 系統(tǒng)命令封裝:am, pm, wm, svc ...
  • 自定義命令封裝:my_tool 適配不同平臺(tái)工具差異。
  • 深度定制 zygote挂捅,framework芹助,原因是:app_process 是初始化 zygote 的入口,屬于安卓系統(tǒng)和Framework 啟動(dòng)的一個(gè)關(guān)鍵點(diǎn)闲先。
  • 優(yōu)化開機(jī)流程状土,減少開機(jī)過程耗時(shí)。

補(bǔ)充:

  • 補(bǔ)充1:
    • 執(zhí)行 app_process伺糠,每次運(yùn)行 Java 程序時(shí)蒙谓,系統(tǒng)都會(huì)給其分配一個(gè)pid,并且進(jìn)程名是app_process训桶,通過追蹤 pidppid累驮,可以發(fā)現(xiàn) fork 進(jìn)程的大致流程為:/init --> /sbin/adbd -–> /system/bin/sh --> app_process,不同的安卓版本舵揭,會(huì)有差異谤专,dalvikart 也不完全相同。例如在 Android 9.0 的設(shè)備上午绳,流程則變?yōu)?/init --> zygote -->當(dāng)前程序置侍。
  • 補(bǔ)充2:
    • app_process 啟動(dòng)的 Java 程序擁有shell級(jí)別的權(quán)限,所以像系統(tǒng)中的 am, pm, wm, svc 等程序才能執(zhí)行。在 /system/bin 目錄下執(zhí)行:cat /system/bin/am 可以看到它其實(shí)并非一個(gè)二進(jìn)制文件蜡坊,而是一個(gè)可執(zhí)行的 shell 腳本杠输,由 app_process 執(zhí)行了 com.android.commands.am.Am "$@",其執(zhí)行原理是在當(dāng)前窗口秕衙,將參數(shù)傳遞給 jvm蠢甲,找到 Am 類的主函數(shù),進(jìn)行執(zhí)行据忘。

      console:/ # cat /system/bin/am
      #!/system/bin/sh
      
      if [ "$1" != "instrument" ] ; then
          cmd activity "$@"
      else
          base=/system
          export CLASSPATH=$base/framework/am.jar
          exec app_process $base/bin com.android.commands.am.Am "$@"
      fi
      console:/ #
      
    • 在 java 程序中執(zhí)行 shellcmd 是可行的鹦牛,分享一個(gè)可執(zhí)行的案例,使用命令為:pm -l

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * 2019-05-18
 * java code for shell command demo, pm -l 
 */
public class DemoShellCmd {
    public static void main(String[] args) {
        System.out.println("");
        System.out.println("");
        System.out.println("DemoShellCmd::PMS 程序開始執(zhí)行...");
        String cmd = "pm -l";
        try {
            Process exec = Runtime.getRuntime().exec(cmd);
            BufferedReader br = new BufferedReader(new InputStreamReader(exec.getInputStream()));
            String readLine=br.readLine();
            while(readLine!=null){
                System.out.println(readLine);
                readLine=br.readLine();
            }
            br.close();
            exec.destroy();
            exec=null;
            System.out.println("");
            System.out.println("");
            System.out.println("DemoShellCmd::PMS 程序執(zhí)行完成");
        } catch (IOException e) {
            System.out.println("DemoShellCmd::PMS 程序執(zhí)行異常");
            e.printStackTrace();
        }
    }
}
  • 補(bǔ)充3:
    • 進(jìn)程狀態(tài)如何查看: cat /proc/$pid/status 即可若河,其中 /proc 下遍布著系統(tǒng)運(yùn)行過程中能岩,所有進(jìn)程 id 的信息,此文件夾下可以瀏覽你關(guān)注的進(jìn)行狀態(tài)萧福,內(nèi)存信息拉鹃,線程信息...

小結(jié):

  1. 如上總結(jié)了 DVM 執(zhí)行 java 程序的三種工具,也是谷歌早期調(diào)試 java 程序的重要工具鲫忍。其中 dvz 工具暫未發(fā)現(xiàn)系統(tǒng)中有集成膏燕,也少有此工具的相關(guān)介紹。重要的是要學(xué)會(huì)使用 dalvikvimapp_process 工具悟民,并對(duì) app_process 的觸發(fā)和流程做拓展坝辫,在源碼中理解它的工作過程。

  2. 歡迎大家下方評(píng)論區(qū)留言射亏,拍磚近忙,交流。

  3. qq 郵箱: 1281641968@qq.com

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末智润,一起剝皮案震驚了整個(gè)濱河市及舍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌窟绷,老刑警劉巖锯玛,帶你破解...
    沈念sama閱讀 206,013評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異兼蜈,居然都是意外死亡攘残,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門为狸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來歼郭,“玉大人,你說我怎么就攤上這事辐棒〔≡” “怎么了姊途?”我有些...
    開封第一講書人閱讀 152,370評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)知态。 經(jīng)常有香客問我,道長(zhǎng)立叛,這世上最難降的妖魔是什么负敏? 我笑而不...
    開封第一講書人閱讀 55,168評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮秘蛇,結(jié)果婚禮上其做,老公的妹妹穿的比我還像新娘。我一直安慰自己赁还,他們只是感情好妖泄,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,153評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著艘策,像睡著了一般蹈胡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上朋蔫,一...
    開封第一講書人閱讀 48,954評(píng)論 1 283
  • 那天罚渐,我揣著相機(jī)與錄音,去河邊找鬼驯妄。 笑死荷并,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的青扔。 我是一名探鬼主播源织,決...
    沈念sama閱讀 38,271評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼微猖,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼谈息!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起励两,我...
    開封第一講書人閱讀 36,916評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤黎茎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后当悔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體傅瞻,經(jīng)...
    沈念sama閱讀 43,382評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,877評(píng)論 2 323
  • 正文 我和宋清朗相戀三年盲憎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嗅骄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,989評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡饼疙,死狀恐怖溺森,靈堂內(nèi)的尸體忽然破棺而出慕爬,到底是詐尸還是另有隱情,我是刑警寧澤屏积,帶...
    沈念sama閱讀 33,624評(píng)論 4 322
  • 正文 年R本政府宣布医窿,位于F島的核電站,受9級(jí)特大地震影響炊林,放射性物質(zhì)發(fā)生泄漏姥卢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,209評(píng)論 3 307
  • 文/蒙蒙 一渣聚、第九天 我趴在偏房一處隱蔽的房頂上張望独榴。 院中可真熱鬧,春花似錦奕枝、人聲如沸棺榔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,199評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)症歇。三九已至,卻和暖如春薄声,著一層夾襖步出監(jiān)牢的瞬間当船,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,418評(píng)論 1 260
  • 我被黑心中介騙來泰國(guó)打工默辨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留德频,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,401評(píng)論 2 352
  • 正文 我出身青樓缩幸,卻偏偏與公主長(zhǎng)得像壹置,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子表谊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,700評(píng)論 2 345