Kotlin进阶指南 - Parcelable序列化

发布时间 2023-04-26 16:16:57作者: cps666

使用Kotlin期间,涉及到对象传递的时候,都需要使用到序列化,以前我习惯Serializable,但是Kotlin中有些框架必须使用Parcelable序列化方式,然后就学习了一下对象序列化插件(注解),毕竟插件效率高一些,有时间我会记录一下原始方法实现Parcelable的写法,最后…遇到一些问题…记录一些解决方式…

你有没有遇到过以下几个异常?

  • How to ignore fields when using @Parcelize annotation in Kotlin
  • Kotlin - How to parcelize an object variable
  • Type is not directly supported by 'Parcelize'. Annotate the parameter type with '@RawValue' if you want it to be serialized using 'writeValue()'
  • This is not support directly by 'Parcelize', Annotate the parameter type with @RawValue if you want it to be serialized using 'writeValue'

百尺竿头

        • 先知
          • 导包注意
          • 使用注意
          • build.gradle(app)实现插件的俩种方式
        • kotlin-android-extensions 插件
        • kotlin-parcelize 插件
        • 兴趣扩展
          • 如何在@Parcelize注解时忽略字段?

先知

整篇主讲kotlin-android-extensions、kotlin-parcelize序列化插件,早期创建kotlin项目时会自动引入kotlin-android-extensions,如果没有引入的话不可使用 @Parcelize注解,不过现在创建的kotlin项目一般都不会再自动引入!主要因为kotlin-android-extensions 插件的另一项功能:通过id去读取控件是会有额外的开销.所以官方已经不推荐这种写法了.

故此如果我们只用"Parcelable序列化"的话,只用“kotlin-parcelize”序列化插件 即可

Parcelable序列化方面,如果没有使用早期的kotlin-android-extensions插件,那么就需要单独引入kotlin-parcelize 插件,但是前提你kt的编译版本为1.4.20,如果是之前的版本,引入’kotlin-parcelize’则会报错

导包注意
  • 使用 kotlin-android-extensions 插件 导包时导入 import kotlinx.android.parcel.ParcelizeAs4.0、4.1 后均已过时
  • 使用 kotlin-parcelize 插件 导包时导入 import kotlinx.parcelize.Parcelize
使用注意

kotlin-android-extensions 插件kotlin-parcelize 插件 不可同时使用!!!

否则 - 报错: 'kotlin-parcelize' can't be applied together with 'kotlin-android-extensions'

原因

  • kotlin-android-extensions 插件 包含 kotlin-parcelize 插件的功能,功能重叠了
  • kotlin-parcelize 插件是独立存在的,本质还是同种相斥的结果,故只能二选一
build.gradle(app)实现插件的俩种方式
  • apply plugin(单插件)

    apply plugin: 'kotlin-android-extensions'

  • plugins + id(多插件)

    plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-kapt'
    id 'kotlin-android-extensions'
    id 'com.alipay.apollo.baseline.config'
    }

友情提示:plugin、plugins 设置位置

在这里插入图片描述


kotlin-android-extensions 插件

提示:kotlin-android-extensions 有很多功能,序列化只是其中的一部分功能而已!!!

在第一行代码第三版中有提到kotlin-android-extensions扩展插件, 但是在as4.0之后便被抛弃,不过可能为了兼容性,这款插件依旧是可以被引入的

build.gradle(Project)

buildscript {
    ext.kotlin_version = '1.4.32'
    repositories {
        google()
        jcenter()
    }
    dependencies {
      classpath "com.android.tools.build:gradle:4.2.1"
      classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
      //我未引入
      classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
    }
}

build.gradle(app)

plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-kapt'
    id 'kotlin-android-extensions'
    id 'com.alipay.apollo.baseline.config'
	// 以下为 序列化专用框架
    //id 'kotlin-parcelize'
}

序列化

  • 定义实体类

  • 添加 @Parcelize 注解

  • 实现Parcelable 接口

  • 注解导包 → kotlinx.android.parcel.Parcelize

    import android.os.Parcelable
    import kotlinx.android.parcel.Parcelize

    @Parcelize
    class PersonText(
    var name: String,
    var age: Int
    ):Parcelable


kotlin-parcelize 插件

build.gradle(app)

 /*序列化插件*/
 apply plugin: 'kotlin-parcelize'

序列化

  • 定义实体类

  • 添加 @Parcelize 注解

  • 实现Parcelable 接口

  • 注解导包 → kotlinx.parcelize.Parcelize

    import android.os.Parcelable
    import kotlinx.parcelize.Parcelize

    @Parcelize
    data class UserInfo(var name: String, var age: String) : Parcelable

兴趣扩展

如何在@Parcelize注解时忽略字段?

在stack overflow 中有一个这样的问题:How to ignore fields when using @Parcelize annotation in Kotlin,自我翻译过来的意思就是在Kt@Parcelize如何忽略字段

大致翻一下原文所遇问题和解决方式

使用下方序列化后,报出:Type is not directly supported by 'Parcelize'. Annotate the parameter type with '@RawValue' if you want it to be serialized using 'writeValue()'

@Parcelize
data class LeaderboardState(
    val progressShown: Boolean = true,
    val pagedList: PagedList<QUser>? = null
) : Parcelable

然后使用@Transient依旧报错

@Parcelize
data class LeaderboardState(
    val progressShown: Boolean = true,

    //Same error
    @Transient
    val pagedList: PagedList<QUser>? = null
) : Parcelable

使用@IgnoredOnParcel依旧报错

@Parcelize
data class LeaderboardState(
    val progressShown: Boolean = true,

    //Same error plus lint error on annotation
    @IgnoredOnParcel
    val pagedList: PagedList<QUser>? = null
) : Parcelable

最终作者采纳了一个答案,如下

使用常规类并将属性移出主要构造函数

@Parcelize
class LeaderboardState(
    val progressShown: Boolean = true,
    pagedList: PagedList<QUser>? = null
) : Parcelable {
    @IgnoredOnParcel
    val pagedList: PagedList<QUser>? = pagedList
}

这显然是唯一的解决方案。确保根据需要覆盖equals,hashCode,toString,copy等,因为它们不会为常规类定义。

编辑:这是另一个解决方案,因此您不会丢失数据类的功能,也不会丢失自动打包。我在这里使用一般示例。

data class Person(
    val info: PersonInfo
    val items: PagedList<Item>? = null)

@Parcelize
data class PersonInfo(
    val firstName: String,
    val lastName: String,
    val age: Int
) : Parcelable

您只保存Person.info并从中重新创建它。