java.lang.VerifyError IllformedLocaleException

java.lang.VerifyError IllformedLocaleException,java,android,exception,verifyerror,Java,Android,Exception,Verifyerror,我有以下父方法,在所有情况下都由不同的API级别使用: 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 { retu

我有以下父方法,在所有情况下都由不同的API级别使用:

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+的代码

当我在 W/dalvikvm:VFY:无法解析异常类6232 (Ljava/util/IllformedLocaleException;)W/dalvikvm:VFY:拒绝 0x0168处的操作码0x0d W/dalvikvm:VFY:已拒绝 Lcom/myapp/android/speech/MyTextToSpeech;。setVoice21 (Ljava/lang/String;Ljava/lang/String;)我与dalvikvm:已拒绝验证程序 Lcom/myapp/android/speech/MyTextToSpeech类

E/AndroidRuntime:致命异常:main java.lang.VerifyError: com/myapp/android/speech/MyTextToSpeech

如果我删除并用一个标准异常替换它,应用程序运行良好,尽管在
setVoice21

更让我困惑的是,
setVoice21
调用以下类

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

    public void buildVoice() {

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

    }
}
该类仅从
setVoice21
引用,但我不必在此处删除对IllformedLocaleException的引用-我可以保留它,应用程序运行正常。。。。莫名其妙

有人能帮我解释一下为什么错误格式的LocaleException会导致此故障吗?异常处理方式是否有所不同?

我先谢谢你

注意-我不确定它是否相关,但我正在以标准方式进行子类化。我担心这会使问题变得复杂,但以防万一

public class MyTextToSpeech extends TextToSpeech {

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

编辑-解决方案确实允许应用程序在不崩溃的情况下运行,但我仍然不知道为什么需要这样一个步骤。在处理API版本控制时,我以前从未采取过这样的措施。

通过从catch参数中删除
IllformedLocaleException
类解决了这个问题。这仍然允许您检查
IllformedLocaleException

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public int setVoice21 (@NonNull final String language, @NonNull final String region) {
    try {
        // try some API 21 stuff
        ...
    } catch (final Exception e) {
        e.printStackTrace();
        if (e instanceof IllformedLocaleException) {
            ...
        }
    }

    ...
}

TL;博士:例外情况是例外。无法捕获类型未知的异常。

以下是基于我对Java/Dalvik的有限知识和常识的推测。把它和盐一起吃。 我找到了吐出失败日志行的方法,并确认了我提到的大多数推测,请参阅下面添加的链接

您的问题似乎是一次加载类,要么加载整个类,要么不加载任何类。 我想验证首先是为了防止一些运行时检查(记住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.TestFragment.setVoice21引用的类“java.util.Locale$Builder”

加载
Locale.Builder
类将是
ClassNotFoundException

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public int setVoice21 (@NonNull final String language, @NonNull final String region) {
    try {
        // try some API 21 stuff
        ...
    } catch (final Exception e) {
        e.printStackTrace();
        if (e instanceof IllformedLocaleException) {
            ...
        }
    }

    ...
}
W/dalvikvm:VFY:无法解析Lcom/test/TestFragment中的新实例5241(Ljava/util/Locale$Builder;)
D/dalvikvm:VFY:在0x0000处替换操作码0x22

然后,在这个不存在的类上,它将尝试调用
方法,该方法通过将。我认为这是可以生存的,因为我在使用支持库时一直看到这些。我认为这里的假设是,如果找不到该类,那么它必须通过
SDK\u INT
检查进行保护。另外,如果它通过了dexing/proguard和其他东西,那么它一定是有意的,并且运行时可以接受
ClassNotFoundException

W/dalvikvm:VFY:无法解析异常类5234(Ljava/util/IllformedLocaleException;)

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

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 */
您确实可以看到
异常表
堆栈映射表
LocalVariableTable
都包含有问题的类,但不包含
Locale$Builder
。这可能是因为构建器没有存储在变量中,但这里要说明的一点是,异常是经过特殊处理的,比普通代码行得到更多的检查

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

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
同样,斯马里:

.catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0
instance-of v1, v0, Ljava/util/IllformedLocaleException;
E/dalvikvm:找不到从方法com.test.TestFragment.setVoice21引用的类“java.util.IllformedLocaleException”
W/dalvikvm:VFY:无法解析Lcom/test/TestFragment中5234(Ljava/util/IllformedLocaleException;)的实例

现在,这与上面的
Locale$Builder
的抱怨是一样的

D/dalvikvm:VFY:在0x000f处替换操作码0x20

替换为“某物”,它没有说:)

另一种可能的解决方法
如果您查看android.support.v4.view.ViewCompat*类,您会注意到并非所有版本都使用这些类。在运行时选择了正确的一个(在
ViewCompat.java
中搜索
static final ViewCompatImpl IMPL
),并且只加载了它。这确保了即使在类加载时,也不会因为缺少类而出现任何奇怪的情况,并且性能良好。您可以使用类似的体系结构来防止该方法加载到早期的API级别。

您的意思是,当您直接调用
setVoice21()
时,您出现了该错误?@razzledazzle no.只有
setVoice
。因此,这个问题很有趣。我在等一个直拨电话。如果还没有,你也可以尝试进行干净的构建。@razzledazzle多次清理和重建。删除该答案以避免混淆。这很有效,谢谢!在<&>21上测试,在<21上运行,在>21上失败并正确捕获。我要是知道为什么会发生这种事就好了!确切的原因对我来说仍然是个谜。谢谢你的提问,我知道