Smali 语法解析,面试必备
作者:互联网
我们首先看一下生成的 Hello.smali
文件内容:
.class public LHello;
.super Ljava/lang/Object;
.source "Hello.java"
# static fields
.field private static HELLO_WORLD:Ljava/lang/String;
# direct methods
.method static constructor <clinit>()V
.registers 1
.prologue
.line 3
const-string v0, "Hello World!"
sput-object v0, LHello;->HELLO_WORLD:Ljava/lang/String;
return-void
.end method
.method public constructor <init>()V
.registers 1
.prologue
.line 1
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
.method public static main([Ljava/lang/String;)V
.registers 3
.prologue
.line 6
sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
sget-object v1, LHello;->HELLO_WORLD:Ljava/lang/String;
invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
.line 7
return-void
.end method
文件头
首先看一下文件头部分:
.class public LHello; // 类名
.super Ljava/lang/Object; // 父类名
.source "Hello.java" // 源文件名称
.class
后面是 访问修饰符和当前类,这里类名用 LHello
表示。那么这个 L
代表什么呢?其实之前的 Class 文件中也出现过这种表示方法,JVM 的字节码指令和 Dalvik 的字节码指令有很多地方都是类似的。Java 中分为基本类型和引用类型,DalviK 对这两种类型分别有不同的描述方法。对于基本类型和 Void 类型,都是用一个大写字母表示。对于引用类型,使用字母 L
加上对象类型的全限定名来表示。具体规则如下表所示:
Java 类型 | 类型描述符 |
---|---|
char | C |
byte | B |
short | S |
int | I |
long | J |
float | F |
double | D |
boolean | Z |
void | V |
对象 | L |
数组 | [ |
基本类型的表示很简单,int 用 I
表示即可。对象的表示,如上图中父类 Object 的表示方法 Ljava/lang/Object;
,再比如 String 类型,就用 Ljava/lang/String
表示。
对于数组,DalviK 有特殊的表示方法 [
后面跟上数组元素的类型。int[]
的表示方式就是 [I
, String[]
的表示方法是 [Ljava/lang/String;
。二维数组用 [[
表示,[[Ljava/lang/String
就是指 String[][]
,以此类推。
字段表示
# static fields
.field private static HELLO_WORLD:Ljava/lang/String;
smali 中的字段以 .field
开头,并有 # static field(静态字段)
或者 # instance field(实例字段)
的注释。.field
之后分别是 访问修饰符,字段名称,冒号以及字段类型描述符。这句 smali 就声明了一个 String
类型名称为 HELLO_WORLD
的私有静态字段。
方法表示
smali 中的方法以 .method
开头。Hello.smali
中包含了三个方法,clinit
, init
和 main
方法。main
方法是我们自己编写的,而 clinit
和 init
方法则是 javac 编译时生成的。下面进行逐一分析:
clinit
.method static constructor <clinit>()V
.registers 1
.prologue
.line 3
const-string v0, "Hello World!"
sput-object v0, LHello;->HELLO_WORLD:Ljava/lang/String;
return-void
.end method
clinit
方法会进行静态变量的初始化,静态代码块的执行等操作,该方法在类被加载的时候调用。逐行分析该方法的执行逻辑:
-
.registers 1 :
该方法需要使用的寄存器数量。之前已经提到,DalviK VM 是基于寄存器的,字节码可以使用的虚拟寄存器个数可达 65536 个,每个寄存器 32 位,64 位的数据使用相邻两个寄存器表示。最终,所有的虚拟寄存器都会被映射到真实的物理寄存器上。一般情况下,我们使用字母v
表示局部变量使用的寄存器,使用字母p
表示参数所使用的寄存器,且局部变量使用的寄存器排列在前,参数使用的寄存器排列在后。这里就表示clinit
方法仅使用了一个寄存器。 -
.prologue :
表示逻辑代码的开始处 -
.line 3 :
表示 java 源文件中的行数 -
const-string v0, "Hello World!"
: 将字符串Hello World!
的引用移到寄存器v0
中。 -
sput-object v0, LHello;->HELLO_WORLD:Ljava/lang/String;
: 前缀s
的sput
和sget
指令用于静态字段的读写操作。将寄存器v0
存储的字符串引用赋值给HELLO_WORLD
字段,结合上一句字节码,这里完成了静态变量HELLO_WORLD
的赋值工作,也验证了clinit
方法的确进行了静态变量的初始化。 -
return-void
: 表示该方法无返回值 -
.end method
: 表示方法执行结束
到这里,clinit
方法就执行结束了。下面分析 init
方法。
init
.method public constructor <init>()V
.registers 1
.prologue
.line 1
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
其余各项与 clinit
方法相同,我们直接看执行的代码逻辑:
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
invoke-direct 用于调用非 static 直接方法(也就是说,本质上不可覆盖的实例方法,即 private 实例方法或构造函数)。显然,这里调用的是默认构造函数。
main
.method public static main([Ljava/lang/String;)V
.registers 3
.prologue
.line 6
sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
sget-object v1, LHello;->HELLO_WORLD:Ljava/lang/String;
invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
.line 7
return-void
.end method
最后是 main
方法,从上述 smali 代码我们可以看到 main
方法使用了 3 个寄存器,无返回值(那是肯定的),执行的具体代码是下面三行:
sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
sget-object v1, LHello;->HELLO_WORLD:Ljava/lang/String;
# 总结
Android架构学习进阶是一条漫长而艰苦的道路,不能靠一时激情,更不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。**所以:贵在坚持!**
上面分享的字节跳动公司2021年的面试真题解析大全,笔者还把一线互联网企业主流面试技术要点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。
![](https://www.icode9.com/i/ll/?i=img_convert/00d748fb27f23d73a0a9395a913556d2.png)
**[CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](https://codechina.csdn.net/m0_60958482/android_p7)**
**【Android高级架构视频学习资源】**
诸多细节。
[外链图片转存中...(img-MYLs318M-1630940389273)]
**[CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](https://codechina.csdn.net/m0_60958482/android_p7)**
**【Android高级架构视频学习资源】**
Android部分精讲视频领取学习后更加是如虎添翼!进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!
标签:lang,Ljava,String,必备,method,v0,面试,寄存器,Smali 来源: https://blog.csdn.net/m0_61418142/article/details/120147089