带注释参数的私有方法的 Android java.lang.VerifyError
我有一个非常简单的项目可以编译,但无法在模拟器上启动。问题在于此方法:
private void bar(@Some String a) {} // java.lang.VerifyError
可以避免该问题
private void bar(String a) {} // OK
如果删除注释或更改方法可见性,则
void bar(@Some String a) {} // OK
public void bar(@Some String a) {} // OK
protected void bar(@Some String a) {} // OK
:知道原始方法有什么问题吗?这是 dalvik 错误吗?
如果有人想尝试一下代码,这里是:
Test.java:
public class Test {
private void bar(@Some String a) {}
public void foo() {
bar(null);
}
}
Some.java:
public @interface Some {}
MainActivity.java:
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new Test().foo();
}
}
Stack trace:
ERROR/dalvikvm(1358): Could not find method com.my.Test.bar, referenced from method com.my.Test.foo
WARN/dalvikvm(1358): VFY: unable to resolve direct method 11: Lcom/my/Test;.bar (Ljava/lang/String;)V
WARN/dalvikvm(1358): VFY: rejecting opcode 0x70 at 0x0001
WARN/dalvikvm(1358): VFY: rejected Lcom/my/Test;.foo ()V
WARN/dalvikvm(1358): Verifier rejected class Lcom/my/Test;
DEBUG/AndroidRuntime(1358): Shutting down VM
WARN/dalvikvm(1358): threadid=3: thread exiting with uncaught exception (group=0x4000fe70)
ERROR/AndroidRuntime(1358): Uncaught handler: thread main exiting due to uncaught exception
ERROR/AndroidRuntime(1358): java.lang.VerifyError: com.my.Test
ERROR/AndroidRuntime(1358): at com.my.MainActivity.onCreate(MainActivity.java:13)
ERROR/AndroidRuntime(1358): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1123)
ERROR/AndroidRuntime(1358): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2231)
ERROR/AndroidRuntime(1358): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2284)
ERROR/AndroidRuntime(1358): at android.app.ActivityThread.access$1800(ActivityThread.java:112)
ERROR/AndroidRuntime(1358): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1692)
ERROR/AndroidRuntime(1358): at android.os.Handler.dispatchMessage(Handler.java:99)
ERROR/AndroidRuntime(1358): at android.os.Looper.loop(Looper.java:123)
ERROR/AndroidRuntime(1358): at android.app.ActivityThread.main(ActivityThread.java:3948)
ERROR/AndroidRuntime(1358): at java.lang.reflect.Method.invokeNative(Native Method)
ERROR/AndroidRuntime(1358): at java.lang.reflect.Method.invoke(Method.java:521)
ERROR/AndroidRuntime(1358): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:782)
ERROR/AndroidRuntime(1358): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:540)
ERROR/AndroidRuntime(1358): at dalvik.system.NativeStart.main(Native Method)
I have a very simple project that compiles, but can't be started on Emulator. The problem is with this method:
private void bar(@Some String a) {} // java.lang.VerifyError
The issue can be avoided if annotation removed
private void bar(String a) {} // OK
or the method visibility changed:
void bar(@Some String a) {} // OK
public void bar(@Some String a) {} // OK
protected void bar(@Some String a) {} // OK
Any idea what is wrong with original method? Is this a dalvik bug, or?
If some one whould like to experiment with code, here it is:
Test.java:
public class Test {
private void bar(@Some String a) {}
public void foo() {
bar(null);
}
}
Some.java:
public @interface Some {}
MainActivity.java:
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new Test().foo();
}
}
Stack trace:
ERROR/dalvikvm(1358): Could not find method com.my.Test.bar, referenced from method com.my.Test.foo
WARN/dalvikvm(1358): VFY: unable to resolve direct method 11: Lcom/my/Test;.bar (Ljava/lang/String;)V
WARN/dalvikvm(1358): VFY: rejecting opcode 0x70 at 0x0001
WARN/dalvikvm(1358): VFY: rejected Lcom/my/Test;.foo ()V
WARN/dalvikvm(1358): Verifier rejected class Lcom/my/Test;
DEBUG/AndroidRuntime(1358): Shutting down VM
WARN/dalvikvm(1358): threadid=3: thread exiting with uncaught exception (group=0x4000fe70)
ERROR/AndroidRuntime(1358): Uncaught handler: thread main exiting due to uncaught exception
ERROR/AndroidRuntime(1358): java.lang.VerifyError: com.my.Test
ERROR/AndroidRuntime(1358): at com.my.MainActivity.onCreate(MainActivity.java:13)
ERROR/AndroidRuntime(1358): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1123)
ERROR/AndroidRuntime(1358): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2231)
ERROR/AndroidRuntime(1358): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2284)
ERROR/AndroidRuntime(1358): at android.app.ActivityThread.access$1800(ActivityThread.java:112)
ERROR/AndroidRuntime(1358): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1692)
ERROR/AndroidRuntime(1358): at android.os.Handler.dispatchMessage(Handler.java:99)
ERROR/AndroidRuntime(1358): at android.os.Looper.loop(Looper.java:123)
ERROR/AndroidRuntime(1358): at android.app.ActivityThread.main(ActivityThread.java:3948)
ERROR/AndroidRuntime(1358): at java.lang.reflect.Method.invokeNative(Native Method)
ERROR/AndroidRuntime(1358): at java.lang.reflect.Method.invoke(Method.java:521)
ERROR/AndroidRuntime(1358): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:782)
ERROR/AndroidRuntime(1358): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:540)
ERROR/AndroidRuntime(1358): at dalvik.system.NativeStart.main(Native Method)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这实际上是 Eclipse 3.5 编译器的一个错误(Bug 289576),它发生了变化带有带注释参数的方法的
private
修饰符,以便该方法成为“包私有”方法。因此,您的:在 .class 文件中变为:
更改后的方法仍然由 invokespecial JVM 指令,仅适用于私有方法调用(也适用于其他一些非方法的东西),但令人惊讶的是也适用于“package- Sun/Oracle JVM 上的“私有”方法。
在 Android .class => 期间.dex翻译invokespecial JVM指令转换为invoke-direct Dalvik指令,只能调用私有方法和构造函数。由于
bar()
方法已成为包可见的方法,invoke-direct 找不到它并抛出NoSuchMethodError
。解决方案是使用 Eclipse 3.6+,或 javac 编译器(通过
build.xml
ant 脚本)。This is actually a bug of Eclipse 3.5 compiler (Bug 289576) which changes the
private
modifier of the method with annotated argument so that method becomes a "package-private" one. So your:in the .class file becomes:
The changed method however is still invoked by the invokespecial JVM instruction, which is intended only for private methods invocations (also for some other non-method stuff), but surprisingly works also for "package-private" methods on Sun/Oracle JVM.
During Android .class => .dex translation invokespecial JVM instruction is converted to invoke-direct Dalvik instruction, which can only invoke private methods and constructors. As the
bar()
method has become a package-visible method, invoke-direct can't find it and throwsNoSuchMethodError
.The solution is to use Eclipse 3.6+, or a javac compiler (through
build.xml
ant script).我的猜测是“private void bar(String) {}”被编译器标记为完全可内联,并且从未实际创建。确切地说为什么 foo() 中的引用会发生(与内联)很难说,但注释很可能会破坏编译器的簿记。
(这里的线索是“私有”——私有方法几乎总是内联的良好候选者,尤其是具有空体的方法。)
My guess is that "private void bar(String) {}" is marked fully inlineable by the compiler and never actually created. Precisely why the reference in foo() then occurs (vs inlining) is hard to say, but likely the annotation screws up the compiler's bookkeeping.
(The clue here is "private" -- private methods are almost always good candidates for inlining, especially ones with void bodies.)