这个问题几年前其实就遇见过,但一直以来不清楚是怎么导致以及怎么解决,最近再次遇到这个问题时,有了一些新的发现。
问题及现象
调用 File.listFiles() 方法时,如果这个目录下有名字包含非UTF-8字符的文件,会导致App Crash,堆栈如下类似下面这样:
art/runtime/java_vm_ext.cc:410] JNI DETECTED ERROR IN APPLICATION: input is not valid Modified UTF-8: illegal continuation byte 0
art/runtime/java_vm_ext.cc:410] string: '中�'
art/runtime/java_vm_ext.cc:410] in call to NewStringUTF
art/runtime/java_vm_ext.cc:410] from java.lang.String[] java.io.File.listImpl(java.lang.String)
art/runtime/java_vm_ext.cc:410] "main" prio=5 tid=1 Runnable
art/runtime/java_vm_ext.cc:410] | group="main" sCount=0 dsCount=0 obj=0x75ca5598 self=0xb4764500
art/runtime/java_vm_ext.cc:410] | sysTid=17110 nice=0 cgrp=default sched=0/0 handle=0xb6f47b50
art/runtime/java_vm_ext.cc:410] | state=R schedstat=( 381765782 63962973 158 ) utm=31 stm=7 core=3 HZ=100
art/runtime/java_vm_ext.cc:410] | stack=0xbe47e000-0xbe480000 stackSize=8MB
art/runtime/java_vm_ext.cc:410] | held mutexes= "mutator lock"(shared held)
art/runtime/java_vm_ext.cc:410] native: #00 pc 0035cf41 /system/lib/libart.so (_ZN3art15DumpNativeStackERNSt3__113basic_ostreamIcNS0_11char_traitsIcEEEEiP12BacktraceMapPKcPNS_9ArtMethodEPv+116)
art/runtime/java_vm_ext.cc:410] native: #01 pc 0033dd2d /system/lib/libart.so (_ZNK3art6Thread4DumpERNSt3__113basic_ostreamIcNS1_11char_traitsIcEEEEP12BacktraceMap+148)
art/runtime/java_vm_ext.cc:410] native: #02 pc 0024feeb /system/lib/libart.so (_ZN3art9JavaVMExt8JniAbortEPKcS2_+762)
art/runtime/java_vm_ext.cc:410] native: #03 pc 00250587 /system/lib/libart.so (_ZN3art9JavaVMExt9JniAbortVEPKcS2_St9__va_list+54)
art/runtime/java_vm_ext.cc:410] native: #04 pc 000fbe6b /system/lib/libart.so (_ZN3art11ScopedCheck6AbortFEPKcz+30)
art/runtime/java_vm_ext.cc:410] native: #05 pc 00101929 /system/lib/libart.so (_ZN3art11ScopedCheck5CheckERNS_18ScopedObjectAccessEbPKcPNS_12JniValueTypeE.constprop.95+8096)
art/runtime/java_vm_ext.cc:410] native: #06 pc 0010859f /system/lib/libart.so (_ZN3art8CheckJNI12NewStringUTFEP7_JNIEnvPKc+374)
art/runtime/java_vm_ext.cc:410] native: #07 pc 0001415b /system/lib/libjavacore.so (_Z13toStringArrayI13VectorCounter12VectorGetterEP13_jobjectArrayP7_JNIEnvPT_PT0_+122)
art/runtime/java_vm_ext.cc:410] native: #08 pc 000143cb /system/lib/libjavacore.so (???)
art/runtime/java_vm_ext.cc:410] native: #09 pc 0002eb89 /system/framework/arm/boot.oat (Java_java_io_File_listImpl__Ljava_lang_String_2+92)
art/runtime/java_vm_ext.cc:410] at java.io.File.listImpl(Native method)
art/runtime/java_vm_ext.cc:410] at java.io.File.list(File.java:740)
art/runtime/java_vm_ext.cc:410] at java.io.File.listFiles(File.java:782)
因为这个crash是发生在native,无法catch。
分析
有这么几个可疑点:
手机上为什么会出现这种名字的文件?
如果这种文件很容易出现,那么应该有很多类似的异常出现,但从实际情况看,只有很少的人遇到过这个问题。
为什么文件管理器不会crash?虽然它显示的文件名也有乱码。
报出这个问题的都是在测试机上,没遇到过线上报出这种错误。
在stackoverflow上有人提过这种问题,有人给出的回答是把apk签名就解决了,我当时觉得这种说法完全没有依据,也有人提到这样无法解决。
之前我个人的看法是,这些文件是由于adb的某些操作导致的,如 adb push 等,可能对中文文件名支持不太好,印象中之前在用 adb push 将中文文件名文件push到手机上时偶尔会出现类似的事,但年代太久远已经无法去求证以及知道当时adb的版本了。
进展
昨天再次遇到了这个问题,是某车的同学反馈的,情况跟我之前的推测很一致,也是在测试机上, adb push 了一个中文名的文件,然后由于这个文件crash了。但是奇怪的是,某些App同样调用了 listFiles() ,却不会crash。
后来跟某车的同学一起,有了一些进展。在 AndroidManifest.xml 中加上 android:debuggable="false" 后,就不会crash了。这个可能是stackoverflow上有人误认为签名打包后就不会有问题了的原因。
但是,为什么加上 android:debuggable="false" 就不会crash了呢?是因为非debuggable的话虚拟机不会往上抛出这个异常吗?具体原因仍然存疑,以后有时间再研究一下吧。