如何从Dalvik字节码角度优化安卓编码实现高效处理?

摘要:目录静态属性与this指针字段与局部变量final属性与编译优化内部类与桥接方法匿名类与Lambda小结 安卓开发中,JavaKotlin等高级语言被编译成.class字节码,之后通过dxd8、r8等工具编译成dex文件(Dalvik字
目录静态属性与this指针字段与局部变量final属性与编译优化内部类与桥接方法匿名类与Lambda小结 安卓开发中,Java/Kotlin等高级语言被编译成.class字节码,之后通过dx/d8、r8等工具编译成dex文件(Dalvik字节码),打包到APK中。安卓通过ART或者DalvikVM加载运行Dalvik字节码。因此,对于安卓编码,Dalvik字节码层面相比.class字节码层面更有指导意义。 静态属性与this指针 静态属性(用static关键字修饰,包括字段以及方法)是类的属性,而非静态属性则是实例的属性。由于非静态属性往往跟实例绑定,静态属性的访问不存在实例,需要的参数更少。 DalvikVM是基于寄存器的指令集,每个方法内的pn寄存器都是参数寄存器,而vn都是本地寄存器,非静态方法中的p0表示this指针。 如下代码是读取类a.b.C的非静态字段bool和静态字段sbool,可以看到在读取非静态字段时会从p0(this)进行引用,而读取sbool则不需要。 iget-boolean v0, p0 La/b/C;->bool:Z sget-boolean v1 La/b/C->sbool:Z 另外,对于代码调用,静态方法使用invoke-static,不需要传入实例本身;而非静态方法使用invoke-virtual调用,即便是从p0(this)调用自己的方法,也需要传入实例。 .method call()V .registers 2 invoke-virtual {p0}, La/b/C;->getBool()Z move-result-object v0 invoke-static La/b/C;->sgetBool()Z move-result-object v0 return-void .end method .method getBool()Z .registers 2 sget-boolean v0, La/b/C;->sbool:Z return v0 .end method .method static sgetBool()Z .registers 1 sget-boolean v0, La/b/C;->sbool:Z return v0 .end method 可以看到,在调用getBool和sgetBool时,由于方法是否静态的差别,其调用参数也有差别:虽然二者的字节码指令一致,仅使用寄存器v0,但非静态方法调用时仍需传参p0。 另外,考虑以下调用: a.b.C obj = null; obj.sgetBool(); obj.getBool(); // NullPointerException 其中obj为一个空值,其在调用非静态方法getBool时必然抛出NullPointerException异常,但却可以安全调用静态方法sgetBool,因为编译器编译后会直接换作obj的类进行invoke-static静态调用,与实例本身无关。 因此,在不考虑子类重写以及使用this时,尽量用static修饰方法。 字段与局部变量 前面说到Dalvik字节码是基于寄存器的指令集,经过ART的AOT/JIT后也更方便生成机器码,这与基于堆栈的.class字节码不同。局部变量在生成Dalvik字节码时往往都用寄存器表示,因此,在安卓开发中使用局部变量时直接当作寄存器即可,不必像堆栈型JVM那样考虑堆栈操作的开销。
阅读全文