编译插桩学习小计
1:编译插桩的场景
编译插桩的技术很好玩,同时学会这项操作,可以随心所欲的控制代码,满足不同场景的需求,比如
butterKnife,Dagger这些注解生成的框架,代码隔离了复杂的内部实现,让开发可以更加简单和搞笑,减少手工重复工作的劳动力,同时降低了出错的可能性。
a:代码监控:我们可以通过插桩技术实现各种各样的性能监控,比如trace的监控
b:代码修改:我们可以进行无痕埋点
c:代码分析:比如findBugs这个第三方代码检查工具
2:编译插桩是哪个流程插入
a: java文件,比如butterKnife是会生成class文件的
b:class字节码,对字节码的操作更加强大,应用范围也很广,我们可以操作.class文字节码和.dex的字节码
在不同的平台,对于Java平台,内部运行的是Java字节码,而针对android这种嵌入式平台,为了优化性能,Android虚拟机运行的是dex文件,google为其设计了一种dalvik字节码,虽然增加了指令的长度,却缩减了指令的数量,执行会更快
那这两个文件有什么区别呢,来看下下面这个简单的文件
public class Test {
public static void main ( String[] args ) {
System.out.println ( "wo shi allen" );
}
}
通过javap -v Test
public class com.testasm.Test
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#20 // java/lang/Object."":()V
#2 = Fieldref #21.#22 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #23 // wo shi allen
#4 = Methodref #24.#25 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #26 // com/testasm/Test
#6 = Class #27 // java/lang/Object
#7 = Utf8
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcom/testasm/Test;
#14 = Utf8 main
#15 = Utf8 ([Ljava/lang/String;)V
#16 = Utf8 args
#17 = Utf8 [Ljava/lang/String;
#18 = Utf8 SourceFile
#19 = Utf8 Test.java
#20 = NameAndType #7:#8 // "":()V
#21 = Class #28 // java/lang/System
#22 = NameAndType #29:#30 // out:Ljava/io/PrintStream;
#23 = Utf8 wo shi allen
#24 = Class #31 // java/io/PrintStream
#25 = NameAndType #32:#33 // println:(Ljava/lang/String;)V
#26 = Utf8 com/testasm/Test
#27 = Utf8 java/lang/Object
#28 = Utf8 java/lang/System
#29 = Utf8 out
#30 = Utf8 Ljava/io/PrintStream;
#31 = Utf8 java/io/PrintStream
#32 = Utf8 println
#33 = Utf8 (Ljava/lang/String;)V
{
public com.testasm.Test();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
LineNumberTable:
line 10: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/testasm/Test;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String wo shi allen
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 13: 0
line 14: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 args [Ljava/lang/String;
}
SourceFile: "Test.java"
再来看下dex中的test的文件,本来想dexdump -d class.dex的,但是生成的信息太多了,所以这边是通过javac先生成class文件,再通过.\dexdump.exe -d E:\software\sdk\build-tools\28.0.3\test.dex生成下面的文件,在生成class文件之前,去掉了包名,
不然会报这个错
class name (com/testasm/Test) does not match path (C:/Users/10067835/Downloads/democs/TestAsm/app/build/intermediates/classes/release/com/testasm/Test.class)
...while parsing C:/Users/10067835/Downloads/democs/TestAsm/app/build/intermediates/classes/release/com/testasm/Test.class
最后生成的结果是下面这样的
Class #0 -
Class descriptor : 'LTest;'
Access flags : 0x0001 (PUBLIC)
Superclass : 'Ljava/lang/Object;'
Interfaces -
Static fields -
Instance fields -
Direct methods -
#0 : (in LTest;)
name : ''
type : '()V'
access : 0x10001 (PUBLIC CONSTRUCTOR)
code -
registers : 1
ins : 1
outs : 1
insns size : 4 16-bit code units
000130: |[000130] Test.:()V
000140: 7010 0300 0000 |0000: invoke-direct {v0}, Ljava/lang/Object;.:()V // method@0003
000146: 0e00 |0003: return-void
catches : (none)
positions :
0x0000 line=10
locals :
0x0000 - 0x0004 reg=0 this LTest;
#1 : (in LTest;)
name : 'main'
type : '([Ljava/lang/String;)V'
access : 0x0009 (PUBLIC STATIC)
code -
registers : 3
ins : 1
outs : 2
insns size : 8 16-bit code units
000148: |[000148] Test.main:([Ljava/lang/String;)V
000158: 6200 0000 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0000
00015c: 1a01 0d00 |0002: const-string v1, "wo shi allen" // string@000d
000160: 6e20 0200 1000 |0004: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0002
000166: 0e00 |0007: return-void
catches : (none)
positions :
0x0000 line=13
0x0007 line=14
locals :
Virtual methods -
source_file_idx : 6 (Test.java)
内容太多了,附上一个链接
https://blog.csdn.net/tabactivity/article/details/78950379
他们之间的差别
a:体系结构,Java虚拟机基于栈实现,Android虚拟机基于寄存器实现,在arm平台,寄存器的性能高于栈
b:格式结构,class文件,每个文件都有单独的常量池,对于dex文件,他们公用相同的常量池,所以结构更紧凑,大大的减少了面积
c:指令优化,相同的代码,dex字节码比java的字节码要多很多
内容太长了,后续插桩的集中方式,再加