Android 不想和你说话,抛了个 java.lang.VerifyError
一个奇怪的崩溃
E/AndroidRuntime(22035): FATAL EXCEPTION: main
E/AndroidRuntime(22035): java.lang.VerifyError: com/sample/FileUtils
E/AndroidRuntime(22035): at com.sample.App.onCreate(App.java:16)
E/AndroidRuntime(22035): at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:999)
E/AndroidRuntime(22035): at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4220)
E/AndroidRuntime(22035): at android.app.ActivityThread.access$1300(ActivityThread.java:137)
E/AndroidRuntime(22035): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1262)
E/AndroidRuntime(22035): at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime(22035): at android.os.Looper.loop(Looper.java:137)
E/AndroidRuntime(22035): at android.app.ActivityThread.main(ActivityThread.java:4819)
E/AndroidRuntime(22035): at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime(22035): at java.lang.reflect.Method.invoke(Method.java:511)
E/AndroidRuntime(22035): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
E/AndroidRuntime(22035): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
E/AndroidRuntime(22035): at dalvik.system.NativeStart.main(Native Method)
为什么说奇怪?一般地,java.lang.VerifyError 是说 JVM 在加载一个类时,会去校验类的正确性,只有类文件不合法才会报这个Error。
比如,一个类试图extends一个标记为final的类,或者试图override final方法(发生在外部依赖类改变声明且应用没有完整重新编译的情况下)。
Android 中会发生这种情况的,一般是需要兼容API的时候,比如用到了高版本SDK中有的类,低版本没有,或者使用高版本API中有低版本没有的方法。
然而这个FileUtils类在com.sample.App中使用时候并没有用到与Android 版本相关的兼容性方法。
百思不得其解
Debug,有时候看堆栈是不够的,还需要查看Logcat中一些有用的上下文
W/dalvikvm(22035): VFY: unable to resolve static method 13457: Landroid/system/Os;.stat (Ljava/lang/String;)Landroid/system/StructStat;
W/dalvikvm(22035): VFY: unable to resolve exception class 1594 (Landroid/system/ErrnoException;)
W/dalvikvm(22035): VFY: unable to find exception handler at addr 0xe
W/dalvikvm(22035): VFY: rejected Lcom/sample/FileUtils;.getUid (Ljava/lang/String;)I
W/dalvikvm(22035): VFY: rejecting opcode 0x0d at 0x000e
W/dalvikvm(22035): VFY: rejected Lcom/sample/FileUtils;.getUid (Ljava/lang/String;)I
W/dalvikvm(22035): Verifier rejected class Lcom/sample/FileUtils;
Log也似乎与平常使用高版本SDK类时的兼容性警告类似:
W/dalvikvm(22524): VFY: unable to resolve virtual method 684: Landroid/content/res/Resources;.getColor (ILandroid/content/res/Resources$Theme;)I
W/dalvikvm(22524): VFY: unable to resolve virtual method 686: Landroid/content/res/Resources;.getColorStateList (ILandroid/content/res/Resources$Theme;)Landroid/content/res/ColorStateList;
W/dalvikvm(22524): VFY: unable to resolve virtual method 693: Landroid/content/res/Resources;.getDrawable (ILandroid/content/res/Resources$Theme;)Landroid/graphics/drawable/Drawable;
回到崩溃开始的警告,”unable to resolve static method” 这条日志应该不会是导致VerifyError的元凶。(注:出现这个警告意味着你如果运行时用到了这个方法,运行时将会报错,如InstantiationError、NoSuchMethodError之类)
那么应该是关键的一句:”unable to find exception handler at addr 0xe“,导致后面的”rejected Lcom/sample/FileUtils;.getUid (Ljava/lang/String;)I” 并最终导致”Verifier rejected class Lcom/sample/FileUtils;”
仔细查看 FileUtils 这个类里的方法getUid()是否有try-catch代码块:
1 | (21) |
确实有尝试catch一个低版本不存在的Exception,但问题在于这个方法并没有使用到!!
而且看起来也十分的正常,一般兼容老版本SDK不都是这样的写法吗?为何单单这里会导致FileUtils类“不合法”?
为了证明是这个在低版本不存在的Exception导致的,对该方法里的try-catch做了简单的处理:
1 | try { |
不出所料,警告只剩下了VFY: unable to resolve static method 13457: Landroid/system/Os;.stat (Ljava/lang/String;)Landroid/system/StructStat;而且没有导致VerifyError
what ??????
想必看到现在的你也是一脸问号……

