java.lang.VerifyError IllformedLocaleException [英] java.lang.VerifyError IllformedLocaleException

查看:242
本文介绍了java.lang.VerifyError IllformedLocaleException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  public int setVoice(@ NonNull final String language,@NonNull final String region){
if(Build.VERSION.SDK_INT> = Build.VERSION_CODES.LOLLIPOP){
return setVoice21(language,region);
} else {
return setVoiceDeprecated(language,region);
}
}

setVoice21 这样做:

  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
public int setVoice21 @NonNull final String language,@NonNull final String region){

try {
// try some API 21 stuff
} catch(final IllformedLocaleException e){
e.printStackTrace();
return setVoiceDeprecated(language,region);
}

setVoice21 包含其他需要API 21+的代码,特别是 TextToSpeech.Voice 和< a href =http://developer.android.com/reference/java/util/Locale.Builder.html =nofollow noreferrer> Locale.Builder



当我在设备上运行此代码时, API 21我收到以下错误:


W / dalvikvm:VFY:无法解析异常类6232
(Ljava / util / IllformedLocaleException;)W / dalvikvm:VFY:rejecting
opcode 0x0d at 0x0168 W / dalvikvm:VFY:rejected
Lcom / myapp / android / speech / MyTextToSpeech; .setVoice21
(Ljava / lang / String; Ljava / lang / String;)IW / dalvikvm:验证器被拒绝
class Lcom / myapp / android / speech / MyTextToSpeech;



< AndroidRuntime:FATAL EXCEPTION:main java.lang.VerifyError:
com / myapp / android / speech / MyTextToSpeech


如果我删除 IllformedLocaleException ,并将其替换为标准的异常应用程序运行正常,尽管许多其他参考方法> API21在 setVoice21



为了使我更加困惑, setVoice21 调用以下类

  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
private class TTSVoice {

public void buildVoice(){

try {
//做一些API 21的东西
} catch(final IllformedLocaleException e){
}

}
}

setVoice21 ,但我不必在这里删除对IllformedLocaleException的引用 - 我可以离开它,应用程序运行正常.... Baffled。



任何人都可以帮助我了解为什么IllformedLocaleException导致此失败?是否有异常处理方式不同?



我提前感谢您。



注意 - 我不确定它是否相关,但我是将 TextToSpeech 以标准方式。我担心这可能会使问题复杂化,但是为了防止...

  public class MyTextToSpeech extends TextToSpeech {

public MyTextToSpeech(final Context context,final OnInitListener listener){
super(context,listener);
}
}

编辑 - 解决方法由razzledazzle提供的以下,确实允许应用程序运行而不会崩溃,但是我仍然不是更聪明的为什么这样的一个步骤是必要的。

解决方案

TL; DR:异常是例外的。无法捕获类型不知道的异常。



以下是我对Java /达尔维克和常识。
我发现了一种方法,吐出失败的日志行,并确认了大部分我提到的猜测,请看下面的添加链接。



您的问题似乎是类一次加载,或者是加载整个类,也可以不加载类。
验证首先完成我想防止一些运行时检查(记住Android是资源受限)。



我使用以下代码:

  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
public int setVoice21(@NonNull final String language,@NonNull final String region){
try {
// try some API 21 stuff
new Locale.Builder()。build()。getDisplayVariant();
} catch(final IllformedLocaleException ex){
ex.printStackTrace();
}
return 0;
}

当系统尝试创建一个包含此方法的类的实例时发生以下情况:


E / dalvikvm:找不到方法com.test引用的类java.util.Locale $ Builder。 TestFragment.setVoice21


加载 Locale.Builder 类将是一个 ClassNotFoundException


W / dalvikvm:VFY:无法解析新实例5241(Ljava / util / Locale $ Builder;)在Lcom / test / TestFragment中;

D / dalvikvm:VFY:将代码0x22替换为0x0000


然后在那个不存在的类上,它将尝试调用< init> 方法,通过替换 OP_NEW_INSTANCE OP_NOP 。我认为这将是可以生存的,因为我在使用支持库时看到这些。我认为这里的假设是,如果没有找到类,那么它必须用 SDK_INT 检查来保护。另外,如果它经过dexing / proguard和其他的东西,它一定是有意的,一个 ClassNotFoundException 在运行时是可以接受的。


另一个有问题的类,注意这一次它是一个必须是特殊的异常类。如果您通过以下方式检查Java方法的字节码:

  javap -verbose -l -private -c -s TestFragment.class> ; TestFragment.dis 

public int setVoice21(java.lang.String,java.lang.String);
...
异常表:
从目标类型
0 14 17类java / util / IllformedLocaleException
LocalVariableTable:
开始长度插槽名称签名
18 11 3 ex Ljava / util / IllformedLocaleException;
0 31 0这个Lcom / test / TestFragment;
0 31 1语言Ljava / lang / String;
0 31 2区域Ljava / lang / String;
StackMapTable:number_of_entries = 2
frame_type = 81 / * same_locals_1_stack_item * /
stack = [class java / util / IllformedLocaleException]
frame_type = 11 / * same * /

你可以看到异常表 StackMapTable LocalVariableTable 都包含有问题的类,但不包含 Locale $ Builder 。这可能是因为构建器不存储在变量中,但是从这里可以看出,异常处理是特别处理的,并且比正常代码行得到更多的审查。



通过以下方式在APK上使用BakSmali:

  apktool.bat d -r -f -o .\disassembledapp-debug .apk

.method public setVoice21(Ljava / lang / String; Ljava / lang / String;)I
.prologue
:try_start_0
new-instance v1 ,Ljava / util / Locale $ Builder;
invoke-direct {v1},Ljava / util / Locale $ Builder; - >< init>()V
...
:try_end_0
.catch Ljava / UTIL / IllformedLocaleException; {:try_start_0 ..:try_end_0}:catch_0
...
:catch_0
move-exception v0
.local v0,ex:Ljava / util / IllformedLocaleException;
invoke-virtual {v0},Ljava / util / IllformedLocaleException; - > printStackTrace()V

似乎揭示了一个类似的模式,在这里我们可以看到日志中提到的操作码。请注意, .catch 似乎是一个特殊的指令,而不是一个操作,因为它前面有一个点。我认为这将加强上述审查:它不是一个运行时操作,但是该类需要加载方法中包含的代码。


W / dalvikvm:VFY:在addr 0xe找不到异常处理程序

W / dalvikvm:VFY:rejected Lcom / test / TestFragment; .setVoice21(Ljava / lang / String; Ljava / lang / String;)我


我想这意味着它不能重建什么时候调用哪个 catch 块从异常表 StackMapTable ,因为找不到类来确定父类。这在 getCaughtExceptionType 其中无法解析异常类直接导致无法找到异常处理程序,因为它找不到不存在的异常的常见超类,像} catch(?ex){,所以它不知道要抓到什么。


W / dalvikvm:VFY:拒绝操作码0x0d在0x000e

W / dalvikvm:VFY:rejected Lcom / test / TestFragment; .setVoice21(Ljava / lang / String; Ljava / lang / String; )我


我认为在这一点上,验证者只是放弃了,因为它无法理解 OP_MOVE_EXCEPTION 。这被确认为 getCaughtExceptionType 方法仅在一个地方使用,一个开关。打破这个我们得到拒绝操作码,然后将 goto bail 将调用堆栈上传到被拒绝的类。在bailing之后,错误代码是 VERIFY_ERROR_GENERIC ,映射到 VerifyError 。没有找到真正的JNI异常在哪里,如果它甚至这样工作。


W / dalvikvm:验证者拒绝类Lcom / test / TestFragment;


针对 setVoice21 方法提交了多次拒绝,因此整个班必须被拒绝(这对我来说看起来很苛刻,ART可能在这方面有所不同)。


W / dalvikvm:类init在NewInstance呼叫中失败(Lcom / test / TestFragment;)

D / AndroidRuntime:关闭VM

W / dalvikvm:threadid = 1:线程退出与未捕获的异常(组= 0x41869da0 )

E / AndroidRuntime:FATAL EXCEPTION:main

进程:com.bumptech.glide.supportapp.v3,PID:27649

java.lang.VerifyError :com / test / TestFragment


我猜这是类似于一个 ExceptionInInitializerError 在桌面Java中,当$ sta时抛出/> c $ c> $ / code>在类体中的tic {} 或静态字段初始化器抛出一个 RuntimeException /



为什么 instanceof 工作



使用razzledazzle的变通方法更改这些表包含 java / lang / Exception ,并将依赖关系移动到 IllformedLocaleException 到要在运行时执行的代码:

  0 14 17类java / lang /异常
19:instanceof#34 // class java / util / IllformedLocaleException

同样的Smali:

  .catch Ljava / lang / Exception; {:try_start_0 ..:try_end_0}:catch_0 
v1,v0,Ljava / util / IllformedLocaleException;




E / dalvikvm:找不到类'java.util.IllformedLocaleException ',从方法com.test.TestFragment.setVoice21引用

W / dalvikvm:VFY:无法解析Lcom / test / TestFragment中的instanceof 5234(Ljava / util / IllformedLocaleException;);


现在,与 Locale $ Builder 以上

$ b $相同的投诉b


D / dalvikvm:VFY:替换opcode 0x20在0x000f


更换 OP_INSTANCE_OF 与...有关的东西,不说:)



另一种可能的解决方法



如果你看您将注意到,所有版本都不使用所有这些类。在运行时选择正确的一个(在 ViewCompat.java 中搜索 static final ViewCompatImpl IMPL ),只有加载。这确保即使在类加载的时候,由于缺少类也不会有任何奇怪的情况,并且是性能的。您可以执行类似的架构,以防止该方法加载到较早的API级别。


I have the following parent method, that is used in all cases by various API levels:

public int setVoice (@NonNull final String language, @NonNull final String region){
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        return setVoice21(language, region);
    } else {
        return setVoiceDeprecated(language, region);
    }
}

and setVoice21 does something like this:

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public int setVoice21 ( @NonNull final String language, @NonNull final String region){

    try {
        // try some API 21 stuff
    } catch (final IllformedLocaleException e) {
        e.printStackTrace();
        return setVoiceDeprecated(language, region);
    }

setVoice21 contains other code that requires API 21+ specifically TextToSpeech.Voice and Locale.Builder

When I run this code on a device < API 21 I'm getting the following error:

W/dalvikvm: VFY: unable to resolve exception class 6232 (Ljava/util/IllformedLocaleException;) W/dalvikvm: VFY: rejecting opcode 0x0d at 0x0168 W/dalvikvm: VFY: rejected Lcom/myapp/android/speech/MyTextToSpeech;.setVoice21 (Ljava/lang/String;Ljava/lang/String;)I W/dalvikvm: Verifier rejected class Lcom/myapp/android/speech/MyTextToSpeech;

E/AndroidRuntime: FATAL EXCEPTION: main java.lang.VerifyError: com/myapp/android/speech/MyTextToSpeech

If I remove the IllformedLocaleException and just replace it with a standard Exception, the app runs fine, despite the many other references to methods > API21 within setVoice21

To confuse me yet further, setVoice21 invokes the following class

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private class TTSVoice {

    public void buildVoice() {

        try {
            // Do some API 21 stuff
        } catch (final IllformedLocaleException e) {
        }

    }
}

This class is only referenced from setVoice21, but I do not have to remove the reference to IllformedLocaleException here - I can leave it and the app runs fine.... Baffled.

Can anyone help me out as to why the IllformedLocaleException is causing this failure? Are Exceptions somehow handled differently?

I thank you in advance.

Note - I'm not sure that it is relevant, but I'm subclassing TextToSpeech in a standard way. I fear this may convolute the question, but just in case...

public class MyTextToSpeech extends TextToSpeech {

    public MyTextToSpeech(final Context context, final OnInitListener listener) {
        super(context, listener);
    }
}

EDIT - The workaround provided by razzledazzle below, does allow the app to run without crashing, but I still remain non-the-wiser as to why such a step is necessary. I've never had to take such measures before when dealing with API versioning.

解决方案

TL;DR: Exceptions are exceptional. Can't catch an exception whose type is not known.

The following is most speculation based on my limited knowledge about Java/Dalvik and common sense. Take it with a grain of salt. I found the method that spits out the failing log line and confirmed most of my mentioned speculations, see added links below.

Your problem seems to be that the classes are loaded at once, either the whole class is loaded or none of it. Verification is done first I guess to prevent some runtime checks (remember Android is resource constrained).

I used the following code:

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public int setVoice21(@NonNull final String language, @NonNull final String region) {
    try {
        // try some API 21 stuff
        new Locale.Builder().build().getDisplayVariant();
    } catch (final IllformedLocaleException ex) {
        ex.printStackTrace();
    }
    return 0;
}

When the system was trying to create an instance of the class containing this method the following happened:

E/dalvikvm: Could not find class 'java.util.Locale$Builder', referenced from method com.test.TestFragment.setVoice21

Loading the Locale.Builder class would be a ClassNotFoundException.

W/dalvikvm: VFY: unable to resolve new-instance 5241 (Ljava/util/Locale$Builder;) in Lcom/test/TestFragment;
D/dalvikvm: VFY: replacing opcode 0x22 at 0x0000

Then on that non-existent class it would try to call the <init> method which is prevented by replacing the OP_NEW_INSTANCE with a OP_NOP. I think this would have been survivable, as I see these all the time when using the support library. I think the assumption here is that if the class is not found then it must have been guarded with an SDK_INT check. Also if it went through dexing/proguard and other stuff it must've been intentional and a ClassNotFoundException is acceptable at runtime.

W/dalvikvm: VFY: unable to resolve exception class 5234 (Ljava/util/IllformedLocaleException;)

Another problematic class, notice this time it's an "exception class" which must be special. If you check the Java bytecode for this method via:

javap -verbose -l -private -c -s TestFragment.class > TestFragment.dis

public int setVoice21(java.lang.String, java.lang.String);
    ...
    Exception table:
       from    to  target type
           0    14    17   Class java/util/IllformedLocaleException
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
         18      11     3    ex   Ljava/util/IllformedLocaleException;
          0      31     0  this   Lcom/test/TestFragment;
          0      31     1 language   Ljava/lang/String;
          0      31     2 region   Ljava/lang/String;
    StackMapTable: number_of_entries = 2
      frame_type = 81 /* same_locals_1_stack_item */
        stack = [ class java/util/IllformedLocaleException ]
      frame_type = 11 /* same */

You can indeed see that the Exception table and StackMapTable and LocalVariableTable all contain the problematic class, but not Locale$Builder. This may be because the builder is not stored in a variable, but the point to take from here is that exceptions are handled specially and get more scrutiny than normal lines of code.

Using BakSmali on the APK via:

apktool.bat d -r -f -o .\disassembled "app-debug.apk"

.method public setVoice21(Ljava/lang/String;Ljava/lang/String;)I
.prologue
:try_start_0
new-instance v1, Ljava/util/Locale$Builder;
invoke-direct {v1}, Ljava/util/Locale$Builder;-><init>()V
...
:try_end_0
.catch Ljava/util/IllformedLocaleException; {:try_start_0 .. :try_end_0} :catch_0
...
:catch_0
move-exception v0
.local v0, "ex":Ljava/util/IllformedLocaleException;
invoke-virtual {v0}, Ljava/util/IllformedLocaleException;->printStackTrace()V

seems to reveal a similar pattern, here we can actually see the op-codes mentioned in the log. Notice that .catch seems to be a special instruction, not an operation because it's preceded by a dot. I think this reinforces the scrutiny mentioned above: it's not a runtime operation, but it is required for the class to load the code contained within the methods.

W/dalvikvm: VFY: unable to find exception handler at addr 0xe
W/dalvikvm: VFY: rejected Lcom/test/TestFragment;.setVoice21 (Ljava/lang/String;Ljava/lang/String;)I

I guess this means that it was not able to reconstruct when to call which catch block from the Exception table and StackMapTable because it couldn't find the class to determine the parent classes. This is confirmed in getCaughtExceptionType where "unable to resolve exception class" directly leads to "unable to find exception handler" because it finds no common super-class for a non-existent exception, something like } catch (? ex) { so it doesn't know what to catch.

W/dalvikvm: VFY: rejecting opcode 0x0d at 0x000e
W/dalvikvm: VFY: rejected Lcom/test/TestFragment;.setVoice21 (Ljava/lang/String;Ljava/lang/String;)I

I think at this point the verifier just gave up because it couldn't make sense of the OP_MOVE_EXCEPTION. This is confirmed as that the getCaughtExceptionType method is only used in one place, a switch. Breaking out of that we get "rejecting opcode" then it goto bails up the call stack to "rejected class". After bailing the error code was VERIFY_ERROR_GENERIC which is mapped to VerifyError. Couldn't find where the actual JNI Exception is thrown if it even works that way.

W/dalvikvm: Verifier rejected class Lcom/test/TestFragment;

Multiple rejections were filed against the setVoice21 method and hence the whole class must be rejected (this seems harsh to me, it's possible ART is different in this regard).

W/dalvikvm: Class init failed in newInstance call (Lcom/test/TestFragment;)
D/AndroidRuntime: Shutting down VM
W/dalvikvm: threadid=1: thread exiting with uncaught exception (group=0x41869da0)
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.bumptech.glide.supportapp.v3, PID: 27649
java.lang.VerifyError: com/test/TestFragment

I guess this is similar to an ExceptionInInitializerError in desktop Java which is thrown when a static { } in class body or an static field initializer throws a RuntimeException/Error.

Why instanceof works

Using razzledazzle's workaround changes those tables to include java/lang/Exception, and moves the dependency to IllformedLocaleException into the code to be executed at runtime:

     0    14    17   Class java/lang/Exception
19: instanceof    #34                 // class java/util/IllformedLocaleException

and similarly the Smali:

.catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0
instance-of v1, v0, Ljava/util/IllformedLocaleException;

E/dalvikvm: Could not find class 'java.util.IllformedLocaleException', referenced from method com.test.TestFragment.setVoice21
W/dalvikvm: VFY: unable to resolve instanceof 5234 (Ljava/util/IllformedLocaleException;) in Lcom/test/TestFragment;

Now, it's the same complaint as for Locale$Builder above

D/dalvikvm: VFY: replacing opcode 0x20 at 0x000f

Replacing OP_INSTANCE_OF with ?something?, it doesn't say :)

Another possible workaround

If you look at android.support.v4.view.ViewCompat* classes you will notice that not all those classes are used on all versions. The correct one is chosen at runtime (search for static final ViewCompatImpl IMPL in ViewCompat.java) and only that is loaded. This ensures that even at class load time there won't be any weirdness due to missing classes and is performant. You can do a similar architecture to prevent that method from loading on earlier API levels.

这篇关于java.lang.VerifyError IllformedLocaleException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆