使用DataStore自定义数据类时部分设备触发java.lang.VerifyError崩溃
我在Firebase Crashlytics中发现应用仅在少数设备上出现罕见崩溃,无法复现该问题。崩溃栈信息如下:
Fatal Exception: java.lang.VerifyError Verifier rejected class o3.c:
java.security.cert.X509Certificate[] o3.c.E(java.nio.ByteBuffer,
java.util.HashMap, java.security.cert.CertificateFactory) failed to
verify: java.security.cert.X509Certificate[]
o3.c.E(java.nio.ByteBuffer, java.util.HashMap,
java.security.cert.CertificateFactory): [0x135] register v0 has type
Reference: java.nio.ByteBuffer but expected Precise Reference: byte[]
(declaration of 'o3.c' appears in
/data/app/~~N1_TwW2swHQZzciPS8e0uw==/com.hidden_my_package.myapp-O87GcvydxMif4uP_FrnEXQ==/base.apk)
kotlinx.coroutines.flow.FlowKt__ShareKt.asStateFlow
(FlowKt__Share.kt:38)com.hidden_my_package.datastore.di.DatastoreModule_ProvidePreferencesManagerFactory.providePreferencesManager
(DatastoreModule_ProvidePreferencesManagerFactory.java:420)
com.hidden_my_package.myapp.DaggerMyApplication_HiltComponents_SingletonC$SingletonCImpl$SwitchingProvider.get
(DaggerBVRApplication_HiltComponents_SingletonC.java:420)
我的实现代码
DataStore实现类
class PreferencesManagerImpl( private val context: Context, private val dataStore: DataStore<Preferences>, lifecycleOwner: LifecycleOwner, ) : PreferencesManager { override val preferences: StateFlow<Preferences> = preferencesStateFlow.asStateFlow() ... }
自定义偏好数据类
// the next free proto number = 119 @Serializable data class Preferences( .... @ProtoNumber(62) val preferencesCamera: HashMap<String, PreferencesCamera>, // key - camera id @ProtoNumber(63) val useMiles: Boolean, ... ) { }
// next free proto number 14 @Serializable data class PreferencesCamera( @ProtoNumber(1) val cameraAperture: Float, @ProtoNumber(2) val cameraControlSceneMode: CameraSceneMode?, @ProtoNumber(3) val cameraControlSceneModeNight: CameraSceneMode?, @ProtoNumber(4) val cameraExposure: Int, @ProtoNumber(5) val cameraExposureNight: Int, ... )
Protobuf序列化器
class PreferencesDatastoreSerializer @Inject constructor( @ApplicationContext private val context: Context ) : Serializer<Preferences> { override val defaultValue: Preferences get() = PreferencesDefaults.getDefaultPreferences(context) override suspend fun readFrom(input: InputStream): Preferences { return try { ProtoBuf.decodeFromByteArray<Preferences>(input.readBytes()) } catch (e: Throwable) { e.printStackTrace() throw CorruptionException("The data could not be de-serialized", e) } } @Suppress("BlockingMethodInNonBlockingContext") override suspend fun writeTo(t: Preferences, output: OutputStream) { output.write(ProtoBuf.encodeToByteArray(t)) } } object PreferencesDefaults { fun getDefaultPreferences(context: Context): Preferences { ... return Preferences( ... preferencesCamera = hashMapOf(), ) } ... }
错误信息提到byte数组和HashMap,我的Preferences类不含byte数组但包含HashMap字段,推测问题与Protobuf编解码相关,想请教该问题仅在少数设备出现的可能原因是什么?
可能的原因分析
Android ART版本差异
不同Android版本的ART虚拟机字节码验证逻辑存在差异,部分旧版本(如Android 7.0及以下)的ART对泛型、类型转换的验证规则更严格,甚至存在已知的验证bug。Protobuf生成的编解码类字节码在这些设备上运行时,ART可能错误地将ByteBuffer类型判定为byte[],触发VerifyError。混淆/代码压缩异常
R8或ProGuard的混淆规则可能在少数设备的打包流程中出现异常,导致o3.c类(推测是Protobuf自动生成的编解码类)的字节码被错误修改,破坏了类型匹配关系。尤其是HashMap作为泛型参数时,混淆可能丢失关键类型信息,引发验证失败。Protobuf库兼容性问题
当前使用的Protobuf库版本可能在特定设备环境下存在兼容性漏洞,比如库中的某些字节码指令无法被旧版ART正确解析,导致类型验证逻辑出错。Hilt类加载顺序问题
崩溃发生在Hilt提供PreferencesManager的过程中,少数设备上可能出现类加载顺序异常,Protobuf相关类还未完成初始化就被调用,间接引发字节码验证错误。
排查建议
- 查看崩溃设备的Android版本分布,确认是否集中在特定旧版本区间。
- 更新Protobuf库到最新稳定版本,同时确保DataStore与Protobuf的版本兼容。
- 添加Protobuf生成类的混淆保持规则,比如
-keep class 你的包名.proto.** { *; },避免混淆破坏类型信息。 - 使用旧版本Android模拟器/真机进行测试,尝试复现问题。
内容的提问来源于stack exchange,提问作者user924

