Kotlin使用Gson反序列化JSON失败(仅Android KitKat及以下出现)
解决Android KitKat(API19)上Gson反序列化Kotlin Parcelable Data Class的问题
这个问题的核心是Android 4.4的Dalvik/ART虚拟机对Kotlin编译器生成的字节码兼容性有限,再加上@Parcelize和Gson的无参构造需求之间的冲突。下面给你几个可行的解决方案,按可靠性排序:
方案1:移除@Parcelize,手动实现Parcelable并添加无参构造
这是最稳妥的方案,完全避免@Parcelize生成的字节码在API19上的兼容性问题,同时满足Gson的无参构造需求。
修改Event类:
给所有参数添加默认值(让Kotlin自动生成无参构造),然后手动实现Parcelable接口:
data class Event( var id: Int = 0, var name: String? = null, @SerializedName("permanent") var isPermanent: Boolean = false, @SerializedName("start_time") var startTime: Long? = null, @SerializedName("end_time") var endTime: Long? = null, @SerializedName("map_publication_time") var mapPublicationTime: Long? = null, @SerializedName("allow_punching") var allowPunching: Boolean? = null, var courses: List<Course>? = null ): Parcelable { // 从Parcel读取数据的构造函数 constructor(parcel: Parcel) : this( parcel.readInt(), parcel.readString(), parcel.readByte() != 0.toByte(), parcel.readValue(Long::class.java.classLoader) as? Long, parcel.readValue(Long::class.java.classLoader) as? Long, parcel.readValue(Long::class.java.classLoader) as? Long, parcel.readValue(Boolean::class.java.classLoader) as? Boolean, parcel.createTypedArrayList(Course.CREATOR) ) override fun writeToParcel(parcel: Parcel, flags: Int) { parcel.writeInt(id) parcel.writeString(name) parcel.writeByte(if (isPermanent) 1 else 0) parcel.writeValue(startTime) parcel.writeValue(endTime) parcel.writeValue(mapPublicationTime) parcel.writeValue(allowPunching) parcel.writeTypedList(courses) } override fun describeContents(): Int = 0 companion object CREATOR : Parcelable.Creator<Event> { override fun createFromParcel(parcel: Parcel): Event = Event(parcel) override fun newArray(size: Int): Array<Event?> = arrayOfNulls(size) } }
同时记得给Course类做同样的修改(添加默认值+手动Parcelable),保证Gson能正常反序列化子对象。
方案2:使用Gson Kotlin扩展库优化反序列化
如果不想放弃@Parcelize,可以尝试用Gson官方的Kotlin扩展库,它能更好地处理Kotlin类的构造函数,减少无参构造的依赖。
步骤:
- 添加依赖(对应你的Gson版本,这里以2.10.1为例):
implementation 'com.google.code.gson:gson-kotlin:2.10.1'
- 创建Gson实例时注册Kotlin适配器:
val gson = GsonBuilder() .registerTypeAdapterFactory(KotlinAdapterFactory()) .create() val event = gson.fromJson(jsonResponse, Event::class.java)
- 给Event类的参数添加默认值(即使保留
@Parcelize):
@SuppressLint("ParcelCreator") @Parcelize data class Event( var id: Int = 0, var name: String? = null, @SerializedName("permanent") var isPermanent: Boolean = false, @SerializedName("start_time") var startTime: Long? = null, @SerializedName("end_time") var endTime: Long? = null, @SerializedName("map_publication_time") var mapPublicationTime: Long? = null, @SerializedName("allow_punching") var allowPunching: Boolean? = null, var courses: List<Course>? = null ): Parcelable
不过这个方案在API19上仍可能出现VerifyError,因为@Parcelize生成的Creator字节码还是可能不兼容,所以优先推荐方案1。
方案3:调整编译参数适配API19
通过修改编译参数,让Kotlin生成更兼容旧Android版本的字节码:
在Module级别的build.gradle中添加:
android { compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = "1.8" freeCompilerArgs += [ "-Xjvm-default=all" // 让Kotlin生成兼容Java的默认方法 ] } }
然后给Event类添加@JvmOverloads构造函数:
@SuppressLint("ParcelCreator") @Parcelize data class Event @JvmOverloads constructor( var id: Int = 0, var name: String? = null, @SerializedName("permanent") var isPermanent: Boolean = false, @SerializedName("start_time") var startTime: Long? = null, @SerializedName("end_time") var endTime: Long? = null, @SerializedName("map_publication_time") var mapPublicationTime: Long? = null, @SerializedName("allow_punching") var allowPunching: Boolean? = null, var courses: List<Course>? = null ): Parcelable
这个方案的兼容性不如方案1,但如果想保留@Parcelize可以尝试。
问题根源解释
- API19的Dalvik虚拟机对Kotlin合成的无参构造、
@Parcelize生成的Creator类的字节码支持不完善,导致VerifyError。 - Gson在旧Android版本中,默认无法自动处理没有无参构造的Kotlin data class,而手动添加无参构造后,又和
@Parcelize生成的代码冲突,引发崩溃。
内容的提问来源于stack exchange,提问作者just_user
相关产品推荐
相关产品推荐

