插桩场景和几种方式

编译插桩学习小计

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的字节码要多很多

内容太长了,后续插桩的集中方式,再加

 

你可能感兴趣的:(android)