Android assert 机制

java 中 可以使用assert语法来进行一些判断检,但是 android上 assert似乎不生效, 这是为什么呢。

在将java source code编译成class文件的过程中,如果一个类中使用了assert语法,那么class文件中会生成一个static field。

 final static synthetic Z $assertionsDisabled

在类的静态初始化函数,会生成以下代码

  static <clinit>()V
   L0
    LINENUMBER 20 L0
    LDC Lorg/chromium/base/JavaExceptionReporter;.class
    INVOKEVIRTUAL java/lang/Class.desiredAssertionStatus ()Z
    IFNE L1
    ICONST_1
    GOTO L2
   L1
   FRAME SAME
    ICONST_0
   L2
   FRAME SAME1 I
    PUTSTATIC org/chromium/base/JavaExceptionReporter.$assertionsDisabled : Z
    RETURN

会先判断Class.desiredAssertionStatus()的返回值,如果是false, 设置assertionsDisabled为true,否则,assertionsDisabled为false。

android中 Class.desiredAssertionStatus() 的定义

public boolean desiredAssertionStatus() {
    return false;
}

所以$assertionsDisabled会被设置为true。

真正使用assert的地,会被替换为

L0
    LINENUMBER 55 L0
    GETSTATIC org/chromium/base/JavaExceptionReporter.$assertionsDisabled : Z
    IFNE L1
    INVOKESTATIC "assert method"Z
    IFNE L1
    NEW java/lang/AssertionError
    DUP
    INVOKESPECIAL java/lang/AssertionError.<init> ()V
    ATHROW
   L1
    LINENUMBER 59 L1

如果$assertionsDisabled 为false,则会先判断assert后面的值,如果是true,则会抛出 AssertionError异常,否则就执行后面的代码。

所以在Android要使assert生效,有两种方法:

  1. adb shell setprop debug.assert 1 (只对dalvik虚拟机生,对art虚拟机不生效!)
  2. 使用字节码修改工具,比如asm,用以下代码将赋值$assertionsDisabled的地方替换成POP 指令。
        @Override
        public void visitFieldInsn(int opcode, String owner, String name, String desc) {
            if (opcode == Opcodes.PUTSTATIC && name.equals("$assertionsDisabled")) {
                super.visitInsn(Opcodes.POP); // enable assert
            }
        }

推荐阅读更多精彩内容