Android kotlin序列化之@Parcelize详解与使用

42 篇文章 1 订阅
31 篇文章 0 订阅

一、介绍

        在Android开发过程中,序列化使用概率一直很高。在页面之间传递的对象,需要要使用序列化,常见的序列化:Parcelable、Serialization。

        由于Parcelable在传递压缩比高,效率高,一直被Google官方推荐。在Java语言中,Parcelable可以通过IDE自动生成,但是在kotlin语言下,IDE无法完成自动生成,需要我们手动去填写,而且语言和Java还是有区别。

二、序列化Parcelable

1、插件接入

kotlin在Parcelable的时候需要额外的扩展插件。

新增插件:apply plugin: 'kotlin-android-extensions'

引入插件后,如果你的项目已支持kotlin语言,那就可以正常开发,如果不支持,需要再添加kotlin插件。

三、实战

在kotlin语言中,Parcelable有两种,一种是方法体,还有一种是构造器,而且两种无法混合使用

1、构造器序列化

何为构造器?是将变量申明在构造器中。

@Parcelize
class TestConstructorBean(var name: String = "", var age: Int = 0) : Parcelable 

这种是最简单的,只要继承了Parcelable,类通过@Parcelize申明一下,无须再去编写复杂的映射关系。只需要这么简单的封装即可。

小试牛刀:

 

如果在方法体中新增字段,是否会序列化成功?

新增字段:

@Parcelize
class TestConstructorBean(var name: String = "", var age: Int = 0) : Parcelable{
    var body:String="default"
}

 入参:

接收参数:

分析:

        这个时候,我们会发现,构造器之外的变量都没有值,也就是该字段没有传递过去,在序列化的时候丢失了。所以构造器参数序列化无法将函数体中的变量也添加进去。 

2、函数体序列化

函数体序列化,也就是我们正常定义的,将变量全部在函数体中申明,中没有我们要传递的变量。

新建Bean类:

@Parcelize
class TestBodyParacle() : Parcelable {

    var name: String = ""
    var age: Int = 0


    constructor(parcel: Parcel):this(){
        name=parcel.readString()!!
        age=parcel.readInt()
    }


    companion object : Parceler<TestBodyParacle> {
        override fun create(parcel: Parcel): TestBodyParacle {

            return TestBodyParacle(parcel)
        }

        override fun TestBodyParacle.write(parcel: Parcel, flags: Int) {

            parcel.writeString(name)
            parcel.writeInt(age)
        }
    }

}

同样继承Parcelable 与@Parcelize。

但是,需要重写 Parceler<T>,申明为内部静态,这个T就是当前类,重写create与write两个方法。重写也是可以参考以前。这样就完成了函数体内部的变量序列化。

小试牛刀:

 这样我们就完成了基本的数据序列化。

那么函数体支持构造器混合使用嘛?

这个时候,body为空。

如果想构造器参数也参与序列化怎么办?

只需要将构造器参数添加到write与create中,进行序列化即可。

构造器参数序列化

三、复杂参数序列化

        在实战过程中,Bean的对象不是简单的数据,可能内部已混合了List、map、Bean等数据。这些数据如何参与序列化?

        为什么会特别强调@Parcelize的对象传递?因为Parcelize是通过注解完成的,但是我在测试过程中,发现注解生成有问题,虽然序列化没问题,但是反序列化获取不到对应的值。

        这里面有个BUG,也就是说@Parcelize修复的序列化,如果只是单独的作为一个data可以正常使用,如果作为其他对象的序列化变量,在反序列化时获取不到,但是作为数组类型也是可以获取到的

@Parcelize
class MixParcleEntity() : Parcelable {

    var list = mutableListOf<TestBodyParacle>()
    var entity: TestBodyParacle? = null



    override fun describeContents(): Int {
       return 0
    }

    protected constructor(parcel: Parcel) : this() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            parcel.readParcelableList(list, TestBodyParacle.javaClass.classLoader)
        }
        parcel.readParcelable<TestBodyParacle>(TestBodyParacle.javaClass.classLoader)
    }

    private fun write(parcel: Parcel, flags: Int) {


        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            parcel.writeParcelableList(list, flags)

        }
        parcel.writeParcelable(entity, flags)
 


    }


    companion object : Parceler<MixParcleEntity> {
        override fun create(parcel: Parcel): MixParcleEntity {
            return MixParcleEntity(parcel)
        }

        override fun MixParcleEntity.write(parcel: Parcel, flags: Int) {

            write(parcel, flags)
        }
    }

}

接收页面

启动页面

反序列化异常分析:

为什么会出现这种情况?正常在JAVA序列化CREATOR,通过这个创建者来完成的,kotlin最终还是要编译成Java文件。但是在编译后,出现了异常,也就是在读写出BUG。

正常kotlin序列化

    companion object {
        @JvmField
        val CREATOR = object : Parcelable.Creator<ParcelableBean> {
            override fun createFromParcel(parcel: Parcel): ParcelableBean {
                return ParcelableBean(parcel)
            }

            override fun newArray(size: Int): Array<ParcelableBean?> {
                return arrayOfNulls(size)
            }
        }
    }
编译字节码

 kotlin注解异常情况:

   companion object : Parceler<ParcelizeBean> {
        override fun create(parcel: Parcel): ParcelizeBean {

            return ParcelizeBean(parcel)
        }

        override fun ParcelizeBean.write(parcel: Parcel, flags: Int) {

            parcel.writeString(name)
        }
    }

问题在于Creator生成。这边生成后,在序列化和反序列,调用的ParcelizeBean.Companion.create(in)发生了异常。由于这个文件是自动生成,导致在页面传递出现了异常情况。这种情况只针对单个对象,如果你是数组没有影响

四、复杂数据使用注解序列化方案

有人会问,复杂的数据真的不支持序列化?当然是可以的。但是只能通过构造函数封装数据。

简单数据

@Parcelize
class Student(var name:@RawValue String?="", var map:@RawValue HashMap<*,*>?=null):Parcelable

复杂数据

@Parcelize
class Teacher(var name:String?="",var student: Student?=null,var list:MutableList<Student>?=null):Parcelable

所有需要参与序列化的属性都在构造器中声明

注意:有可能简单数据会报错

Type is not directly supported by ‘Parcelize’ Annotate the parameter type with ‘@RawValue’ if you want it to be serialized using ‘writeValue()’
 

只需要在变量类型前面加上@RawValue

如下:

var name:@RawValue String?="", var map:@RawValue HashMap<*,*>?=null

这样也能解决问题,要保持一致,否则还是会出错。如果不会建议看下面的手动完成序列化

五、总结

1、获取不到数据

        如果在使用过程中,通过注解发生了数据异常,需要检查对象是否序列化了,如果通过构造器的,查看方法体是否参与了,如果是很简单的数据建议构造器,如果是复杂的数据,建议抛弃构造器

2、类中定义子类序列化失败

        如果采用了注解,就不能使用类中定义类这种用法,否则对象获取为null。那用什么?接下来我会介绍抛弃注解,直接使用手写

Android kotlin序列化之Parcelable详解与使用(二)_蜗牛、Z的博客-CSDN博客

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值