lannyf
lannyf

Reputation: 11035

Android, how is the Parcelable serialized/deserialized, does it maintain the reference to the object instance?

Having a fragment it expects an IDataProvider (which is Parcelable) to be passed in through the Fragment's arguments, and with it to get the data from the repository.

this is the DataFragment, which retrieves dataProvider from the arguments via bundle.getParcelable<Parcelable>(KEY_DATA_PROVIDER) as? IDataProvider

    class DataFragment: Fragment() {

        interface IDataProvider : Parcelable {
            fun getDataByUUID(uuid: String): IData?
        }

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            //retainInstance = true

            var bundle = arguments
            var dataProvider: IDataProvider = bundle.getParcelable<Parcelable>(KEY_DATA_PROVIDER) as? IDataProvider

            // the provider is got from bundle.getParcelable
            // and would expect the `IDataRepository` reference kept 
            // in the dataProvider should be lost in the 
            // serialize/deserialize of the Parcelable
            // but it does not, and it is still working to be able to make the call and return the data from the repository

            val data: Data = dataProvider?.getDataByUUID("xxx-yyy-zzz")

            // the data is returned fine (???)
                    ......

        }

        ... ...
    }

this is the activity, which puts an IDataProvider in the arguments of the instance of the DataFragment via Bundle().putParcelable(KEY_DATA_PROVIDER, dataProvider)

    class DataActivity: Activity {

        var dataProvider: DataProvider? = null

        val viewModel = getViewModel()  //get the viewModel which has the dataRepository

        fun createFragment(): Fragment? {

            dataProvider = DataProvider()
            dataProvider?.let {
                dataProvider.repository = viewModel?.getDataRepository()

                val args = Bundle()
                args.putParcelable(KEY_DATA_PROVIDER, dataProvider)  //put the dataProvider in the Bundle with putParcelable

                var dataFragment = DataFragment()
                dataFragment.arguments = args  // set to its arguments
                return contentFragment
            }
            return null
        }

        override fun onDestroy() {
            super.onDestroy()

            dataProvider?.repository = null
        }


        // DataProvider implementation, 
        // it has a reference to a IDataRepository
        // but is not serialized/deserialized when it is parceling

        private var dataProvider: DataProvider? = null
        class DataProvider() : DataFragment.IDataProvider {

            var repository: IDataRepository? = null
            override fun getDataByUUID(uuid: String): IData? {
                return repository?.getData(uuid)
            }

            constructor(parcel: Parcel) : this() {}
            override fun writeToParcel(parcel: Parcel, flags: Int) {}
            override fun describeContents(): Int {
                return 0
            }

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

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

If with the implementation above, it would be expected that the member variable repository in the class DataProvider() : DataFragment.IDataProvider should be lost since there is no code to serialized/deserialized in the writeToParcel()/readFromParcel().

But when rrunning it seems in the fragment when parcel it back from the bundle the member variable repository is still valid.

Anyone knows why, or how the Parcelable is being serialized/deserialized?

Upvotes: 0

Views: 309

Answers (1)

lannyf
lannyf

Reputation: 11035

looks like if using the generated fragment from createFragment()

fun createFragment(): Fragment? {

        dataProvider = DataProvider()
        dataProvider?.let {
            dataProvider.repository = viewModel?.getDataRepository()

            val args = Bundle()
            args.putParcelable(KEY_DATA_PROVIDER, dataProvider)  //put the dataProvider in the Bundle with putParcelable

            var dataFragment = DataFragment()
            dataFragment.arguments = args  // set to its arguments
            return contentFragment
        }
        return null
    }

and do

var bundle = createFragment().arguments
var dataProvider: IDataProvider = bundle.getParcelable<Parcelable>(KEY_DATA_PROVIDER) as? IDataProvider

the bundle is still having the same instance of the contained parcelables, so it still works.

but in the case like os kill and restore the fragment, the parcelable from the new fragment's arguments will have new instance of the parcelable, and that will not have the previous reference anymore.

Upvotes: 1

Related Questions