RealmList with @Parcelize annotation

2020-02-06 04:39发布

问题:

I'm trying to use the new @Parcelize annotation added with Kotlin 1.1.4 with a Realm objet containing a RealmList attribute.

@Parcelize
@RealmClass
open class Garage(
        var name: String? = null,
        var cars: RealmList<Car> = RealmList()
) : Parcelable, RealmModel

As RealmList is not supported by the annotation and assuming that @Parcelize is there specially to avoid creating methods what would be the solution to support RealmList ?

Thanks in advance

回答1:

EDIT:

Using the power of Type Parcelers and @WriteWith annotation, it is possible to create a RealmList type parceler that can handle this scenario for you:

With the following code:

fun <T> Parcel.readRealmList(clazz: Class<T>): RealmList<T>?
    where T : RealmModel,
          T : Parcelable = when {
    readInt() > 0 -> RealmList<T>().also { list ->
        repeat(readInt()) {
            list.add(readParcelable(clazz.classLoader))
        }
    }
    else -> null
}

fun <T> Parcel.writeRealmList(realmList: RealmList<T>?, clazz: Class<T>)
    where T : RealmModel,
          T : Parcelable {
    writeInt(when {
        realmList == null -> 0
        else -> 1
    })
    if (realmList != null) {
        writeInt(realmList.size)
        for (t in realmList) {
            writeParcelable(t, 0)
        }
    }
}

You can define an interface like this:

interface RealmListParceler<T>: Parceler<RealmList<T>?> where T: RealmModel, T: Parcelable {
    override fun create(parcel: Parcel): RealmList<T>? = parcel.readRealmList(clazz)

    override fun RealmList<T>?.write(parcel: Parcel, flags: Int) {
        parcel.writeRealmList(this, clazz)
    }

    val clazz : Class<T>
}

Where you'll need to create a specific parceler for the RealmList<Car> like this:

object CarRealmListParceler: RealmListParceler<Car> {
    override val clazz: Class<Car>
        get() = Car::class.java
}

but with that, now you can do the following:

@Parcelize
@RealmClass
open class Garage(
        var name: String? = null,
        var cars: @WriteWith<CarRealmListParceler> RealmList<Car> = RealmList()
) : Parcelable, RealmModel

And

@Parcelize
@RealmClass
open class Car(
    ..
): RealmModel, Parcelable {
    ...
}

This way you don't need to manually write the Parceler logic.



ORIGINAL ANSWER:

Following should work:

@Parcelize
open class Garage: RealmObject(), Parcelable {
    var name: String? = null
    var cars: RealmList<Car>? = null

    companion object : Parceler<Garage> {
        override fun Garage.write(parcel: Parcel, flags: Int) {
            parcel.writeNullableString(name)
            parcel.writeRealmList(cars)
        }

        override fun create(parcel: Parcel): Garage = Garage().apply {
            name = parcel.readNullableString()
            cars = parcel.readRealmList()
        }
    }
}

As long as you also add following extension functions:

inline fun <reified T> Parcel.writeRealmList(realmList: RealmList<T>?)
    where T : RealmModel,
          T : Parcelable {
    writeInt(when {
        realmList == null -> 0
        else -> 1
    })
    if (realmList != null) {
        writeInt(realmList.size)
        for (t in realmList) {
            writeParcelable(t, 0)
        }
    }
}

inline fun <reified T> Parcel.readRealmList(): RealmList<T>?
    where T : RealmModel,
          T : Parcelable = when {
    readInt() > 0 -> RealmList<T>().also { list ->
        repeat(readInt()) {
            list.add(readParcelable(T::class.java.classLoader))
        }
    }
    else -> null
}

and also

fun Parcel.writeNullableString(string: String?) {
    writeInt(when {
        string == null -> 0
        else -> 1
    })
    if (string != null) {
        writeString(string)
    }
}

fun Parcel.readNullableString(): String? = when {
    readInt() > 0 -> readString()
    else -> null
}