0
点赞
收藏
分享

微信扫一扫

浅谈 Android Dex 文件

左手梦圆 2022-04-21 阅读 137

概述

为什么要了解 Dex 文件

了解了 Dex 文件以后,对日常开发中遇到一些问题能有更深的理解。如:APK 的瘦身、热修复、插件化、应用加固、Android 逆向工程、64 K 《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》开源 方法数限制。

什么是 Dex 文件

在明白什么是 Dex 文件之前,要先了解一下 JVM,Dalvik 和 ART。JVM 是 JAVA 虚拟机,用来运行 JAVA 字节码程序。Dalvik 是 Google 设计的用于 Android平台的运行时环境,适合移动环境下内存和处理器速度有限的系统。ART 即 Android Runtime,是 Google 为了替换 Dalvik 设计的新 Android 运行时环境,在Android 4.4推出。ART 比 Dalvik 的性能更 Android开源项目《ali1024.coding.net/public/P7/Android/git》 好。Android 程序一般使用 Java 语言开发,但是 Dalvik 虚拟机并不支持直接执行 JAVA 字节码,所以会对编译生成的 .class 文件进行翻译、重构、解释、压缩等处理,这个处理过程是由 dx 进行处理,处理完成后生成的产物会以 .dex 结尾,称为 Dex 文件。Dex 文件格式是专为 Dalvik 设计的一种压缩格式。所以可以简单的理解为:Dex 文件是很多 .class 文件处理后的产物,最终可以在 Android 运行时环境执行。

Dex 文件是怎么生成的

java 代码转化为 dex 文件的流程如图所示,当然真的处理流程不会这么简单,这里只是一个形象的显示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lLWLUjTY-1650450411612)(https://user-gold-cdn.xitu.io/2018/11/19/16729fad9dc6dcb3?imageView2/0/w/1280/h/960/ignore-error/1)]

现在来通过一个简单的例子实现 java 代码到 dex 文件的转化。

从 .java 到 .class

先来创建一个 Hello.java 文件,为了便于分析,这里写一些简单的代码。代码如下:

public class Hello {
private String helloString = “hello! youzan”;

public static void main(String[] args) {
Hello hello = new Hello();
hello.fun(hello.helloString);
}

public void fun(String a) {
System.out.println(a);
}
}

在该文件的同级目录下面使用 JDK 的 javac 编译这个 java 文件。

javac Hello

javac 命令执行后会在当前目录生成 Hello.class 文件,Hello.class 文件已经可以直接在 JVM 虚拟机上直接执行。这里使用使用命令执行该文件。

java Hello

执行后应该会在控制台打印出“hello! youzan”

这里也可以对 Hello.class 文件执行 javap 命令,进行反汇编。

javap -c Hello

执行结果如下:

public class Hello {
public Hello();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object.“”😦)V
4: aload_0
5: ldc #2 // String hello! youzan
7: putfield #3 // Field helloString:Ljava/lang/String;
10: return

public static void main(java.lang.String[]);
Code:
0: new #4 // class Hello
3: dup
4: invokespecial #5 // Method “”😦)V
7: astore_1
8: aload_1
9: aload_1
10: getfield #3 // Field helloString:Ljava/lang/String;
13: invokevirtual #6 // Method fun:(Ljava/lang/String;)V
16: return

public void fun(java.lang.String);
Code:
0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_1
4: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
7: return
}

其中 Code 之后都是具体的指令,供 JVM 虚拟机执行。指令的具体含义可以参考 JAVA 官方文档。

从 .class 到 .dex

上面生成的 .class 文件虽然已经可以在 JVM 环境中运行,但是如果要在 Android 运行时环境中执行还需要特殊的处理,那就是 dx 处理,它会对 .class 文件进行翻译、重构、解释、压缩等操作。

dx 处理会使用到一个工具 dx.jar,这个文件位于 SDK 中,具体的目录大致为 你的SDK根目录/build-tools/任意版本 里面。使用 dx 工具处理上面生成的Hello.class 文件,在 Hello.class 的目录下使用下面的命令:

dx --dex --output=Hello.dex Hello.class

执行完成后,会在当前目录下生成一个 Hello.dex 文件。这个 .dex 文件就可以直接在 Android 运行时环境执行,一般可以通过 PathClassLoader 去加载 dex 文件。现在在当前目录下执行 dexdump 命名来反编译:

dexdump -d Hello.dex

执行结果如下(部分区域的含义已经在下面描述):

Processing ‘Hello.dex’…
Opened ‘Hello.dex’, DEX version ‘035’

------ 这里是编写的 Hello.java 的类的信息 ------
Class #0 -
Class descriptor : ‘LHello;’
Access flags : 0x0001 (PUBLIC)
Superclass : ‘Ljava/lang/Object;’
Interfaces -
Static fields -
Instance fields -
#0 : (in LHello;)
name : ‘helloString’
type : ‘Ljava/lang/String;’
access : 0x0002 (PRIVATE)

------ 下面区域描述的是构造方法的信息。7010 0400 0100 1a00 0b00 之类的数字就是方法中的代码翻译成的指令。Dalvik 使用的是16位代码单元,所以这里就是4个数字为一组,每个数字是16进制。invoke-direct 这些是前面指令对应的助记符,也代表着这些指令的真正操作。如果对这些指令转化感兴趣可以去https://source.android.com/devices/tech/dalvik/instruction-formats 查看 ------
Direct methods -
#0 : (in LHello;)
name : ‘’ — 方法名称:这个很明显就是构造方法 —
type : ‘()V’ — 方法原型,()里面表示入参,()后面表示返回值,V代表void—
access : 0x10001 (PUBLIC CONSTRUCTOR) — 方法访问类型 —
code -
registers : 2 — 方法使用的寄存器数量 —
ins : 1 — 方法入参,方法除了我们定义的参数以外,系统还会默认带一个特殊参数 —
outs : 1
insns size : 8 16-bit code units — 指令大小 —
000148: |[000148] Hello.😦)V
000158: 7010 0400 0100 |0000: invoke-direct {v1}, Ljava/lang/Object;.😦)V // method@0004
00015e: 1a00 0b00 |0003: const-string v0, “hello! youzan” // string@000b
000162: 5b10 0000 |0005: iput-object v0, v1, LHello;.helloString:Ljava/lang/String; // field@0000
000166: 0e00 |0007: return-void
catches : (none)
positions :
0x0000 line=1
0x0003 line=2
locals :
0x0000 - 0x0008 reg=1 this LHello;

#1 : (in LHello;)
name : ‘main’
type : ‘([Ljava/lang/String;)V’
access : 0x0009 (PUBLIC STATIC)
code -
registers : 3
ins : 1
outs : 2
insns size : 11 16-bit code units
000168: |[000168] Hello.main:([Ljava/lang/String;)V
000178: 2200 0000 |0000: new-instance v0, LHello; // type@0000
00017c: 7010 0000 0000 |0002: invoke-direct {v0}, LHello;.😦)V // method@0000
000182: 5401 0000 |0005: iget-object v1, v0, LHello;.helloString:Ljava/lang/String; // field@0000
000186: 6e20 0100 1000 |0007: invoke-virtual {v0, v1}, LHello;.fun:(Ljava/lang/String;)V // method@0001
00018c: 0e00 |000a: return-void
catches : (none)
positions :
0x0000 line=5

面试复习笔记:

这份资料我从春招开始,就会将各博客、论坛。网站上等优质的Android开发中高级面试题收集起来,然后全网寻找最优的解答方案。每一道面试题都是百分百的大厂面经真题+最优解答。包知识脉络 + 诸多细节。
节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

《960页Android开发笔记》

《1307页Android开发面试宝典》

包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。

《507页Android开发相关源码解析》

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。
图片转存中…(img-NBdUnndb-1650450411613)]

《507页Android开发相关源码解析》

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

举报

相关推荐

Android Dex分包

Android IO、序列化、dex 随记

0 条评论