0

In the android app there are cases need to pass some data around, crossing activity/fragment, pass to service etc. It has been using the parcelable and put in the intent/bundle. It works fine except get crash on over the one meg limit of the parcelable sometime.

android.os.TransactionTooLargeException: data parcel size 526576 bytes

Trying to see if it could put the content of the parcelable object into a lruCache, so basically replacing the parcelable's saving/loading with its own implementation of using lruCache.

Does this approach have any issue? Or any suggestion/alternative to address the problem?

@ApiSerializable
class  DataItem (
        @SerializedName("uuid")
        var uuid: String = "",

        @SerializedName("image")
        val mainImage: Image?,  //another parcelable type

        @SerializedName("entities")
        var entities: List<EntityInfo>?,


        //......
        // a lot of data
        //......
        //......

) : BaseDataItem(), IData {

    override fun uuid(): String {
        return uuid
    }

    //......


    constructor(parcel: Parcel) : this(
            parcel.readString(), //uuid

            //...
            //...

            parcel.readParcelable(Image::class.java.classLoader),
            mutableListOf<EntityInfo>().apply {
                parcel.readTypedList(this, EntityInfo.CREATOR)
            }) {

    }


    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeString(uuid ?: "")

        //......
        //......

        parcel.writeParcelable(mainImage, flags)
        parcel.writeTypedList(entities)

    }

    override fun describeContents(): Int {
        return 0
    }

    companion object CREATOR : Parcelable.Creator<DataItem> {
        override fun createFromParcel(parcel: Parcel): DataItem {
            return DataItem(parcel)
        }

        override fun newArray(size: Int): Array<DataItem?> {
            return arrayOfNulls(size)
        }
    }
}

The approach is to replace the saving/loading from the parcel part with using lruCache itself:

    // having the cache somewhere
    val dataCache =  LruCache<String, IData>(200)

and only one string member is saved/loaded with the Parcel:

    fun init (copyData: DataItem) {
        // do sopy over from the copyData
    }

    constructor(parcel: Parcel) : this() {
        uuid = parcel.readString(), //uuid

        val _thisCopy = dataCache.get(uuid)

        init(_thisCopy)

    }

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeString(uuid ?: "")

        dataCache.put(uuid, this)
    }
lannyf
  • 9,865
  • 12
  • 70
  • 152
  • 1
    Simple. Don't pass around objects but only an identifier of an object, then in the other screen where you want the object, retrieve it from your database – Zun Apr 19 '19 at 12:06
  • there might be a list of data passing around, getting from database is slow if do it for every single item. and the DataItem could also be some member of other parcelable class there cannot simply replaced with a "identifier" – lannyf Apr 19 '19 at 12:12
  • You're wrong. Databases are fast. Obtaining a list is fast. If your list has no identifier then I highly suggest you change your entire architecture of your application. Start reading this if you're a newb to Android https://developer.android.com/jetpack/docs/guide – Zun Apr 19 '19 at 12:13
  • "and the DataItem could also be some member of other parcelable class there cannot simply replaced with a "identifier"" -- well, it has to be, as you need an identifier as the key to your `LruCache`. Using a cache as part of a repository is perfectly reasonable, if occasionally a bit tricky to get right. – CommonsWare Apr 19 '19 at 12:16
  • Thanks @CommonsWare, yes the "identifier" is the "uuid" within the DataItem, it's just that another Parceable class could have the DataItem as its member, so that class could be doing 'parcel' operation and implicitly parcel the DataItem as well. And sounds this approach is doable but "tricky", any hint on what might be the tricky part to watch out? – lannyf Apr 19 '19 at 12:26
  • @ZUNJAE, database is much slower than memory cache. – lannyf Apr 19 '19 at 12:26
  • You need to ensure that you are updating or invalidating the cache when you make changes to the database. And, you need to make certain that whatever reactive approaches you are using (RxJava, `LiveData`, etc.) emit the changed objects when they change to any active subscribers/observers. – CommonsWare Apr 19 '19 at 12:30
  • @CommonsWare, good point, will look into those use cases, thanks! – lannyf Apr 19 '19 at 12:32
  • Yes, database might be slower than memory cache, but when using bundles you're persisting your objects to your disk. This adds an unnecessary overhead. This reminds me, why not use ViewModels? – Zun Apr 19 '19 at 12:39

1 Answers1

0

You should avoid passing whole Object with large amount of data to your Next activity. So it might possible that your Object can have many data. So sometime system can not handle much data to transfer at a time. Try to use Preferences to store your object data and retrieve the same on other activity.

Please my answer for here: Exception when starting activity android.os.TransactionTooLargeException: data parcel size

Ajay Mehta
  • 843
  • 5
  • 14