5

I've been looking for some time to android architechture components and lately to the Navigation component.

I'm trying to pass as a parameter an object, so the next fragment can retrieve that data, but to do so I'm required to do one of two things:

  1. Pass it through a Bundle, which will make me implement the Parcelable interface to that object.
  2. Use the "Safeargs" plugin which I've tried and it looks like behind the hood makes use of Bundles and requires the implementation of the Parcelable interface anyway.

The thing about these options is that I've read that Parcelable makes use of reflection and it can get quite expensive regarding time


I have also tried to build a "SharedMasterDetailsViewModel" but with no luck since the observable callbacks are not being performed on my newly created Fragment. (I think LiveData performs the callback before my fragment is created)

Here's some code about how I've tried to approach this

SharedSessionViewModel

class SessionSharedViewModel : ViewModel() {


    var sharedSession: LiveData<Session> = MutableLiveData()
        private set

    fun setSession(data: Session) {
        val casted = this.sharedSession as MutableLiveData<Session>
        casted.postValue(data)
    }
}

MasterFragment

override fun onItemClicked(item: Session) {
    sessionSharedViewModel.setSession(item) // Item is a complex object of mine
    [email protected]().navigate(R.id.sessionDetailsFragment)
}

DetailsFragment

class SessionDetailsFragment : Fragment() {

    companion object {
        fun newInstance() = SessionDetailsFragment()
    }

    private lateinit var sharedViewModel: SessionSharedViewModel

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.session_details_fragment, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        Log.d("SESSIONDETAILS","created!")

        super.onActivityCreated(savedInstanceState)
        sharedViewModel = ViewModelProviders.of(this).get(SessionSharedViewModel::class.java)
        sharedViewModel.sharedSession.observe({this.lifecycle},{ handleUI(it!!)})
    }

    fun handleUI(sharedSession: Session) {
        Toast.makeText(activity, "This is inside new activity: ${sharedSession.title()}", Toast.LENGTH_SHORT)
    }
}

My last hope is to serialize my object into a JSON string and reparse that object on the onCreateActivity lyfecycle hook of my Detail fragment but I feel like that is not the proper solution.

In the worst case scenerio I would just pass the id of the object and re-fetch it from the network, but since I already have the info I want to show I'd like to pass it as a parameter.

4
  • 1
    Not a concrete answer - Why dont you use a Room DB to hold your data? Its not an ideal thing to parse JSON and construst an object from that when you can use the Serializable interface to marshall your object the good old java way. Having said that Parcelable is a better choice than Serializable as Parcelable does not use Reflection In-fact Parcelable was created with a motive of having less fingerprint than Serializable. Commented Dec 5, 2018 at 9:20
  • This is a toy project and I haven't got into Room yet, so I didn't consider it. Also I got it working with a static variable elsewhere typed as Any and then cast it back to its type in the creation of the fragment. But this a filthy dirty trick and I don't want to do that. Commented Dec 5, 2018 at 9:23
  • 1
    Your ViewModel approach not working because you creating it with scope of HomeFragment fragment, so when you navigating to another fragment another viewmodel instance created with SessionDetailsFragment scope, you need to create them with activity scope, like this: sharedViewModel = ViewModelProviders.of(activity!!).get(SessionSharedViewModel::class.java) Commented Dec 5, 2018 at 9:25
  • That solves my problem with that approach@Alex, thank you very much. Commented Dec 5, 2018 at 9:36

1 Answer 1

6

TL; DR

You can't.

Actual explanation

First thing; the following statement

The thing about these options is that I've read that Parcelable makes use of reflection and it can get quite expensive regarding time.

IS A LIE

Since you implement Parcelable you're just providing methods on how to serialize and deserialize using basic primitive types: IE: writeBytes, readBytes, readInt, writeInt.

Parcelable does NOT use reflection. Serializable does!

While it's true you are forced to use Parcelable jet brains developed a very useful annotation that takes away the pain of having to write the parcelable implementation called @Parcelize.

Sample usage:

@Parcelize
data class User(val username: String, val password: String) : Parcelable

And now you're able to pass instances of the class without writing a single line of Parcelable implementation.

More info here

Sign up to request clarification or add additional context in comments.

2 Comments

Er, so "you can't"? Or, "now you're able ... just use @Parcelize"?
Yeah, the constraint for me was that I wanted to avoid writing the Parcelable implementation and have everything just work out of the box. So this I just mentioned you just can't. Some time later Kotlin provided the @Parcelize annotation which lifts this work from me which was very acceptable from my point of view. I also thought that Parcelable made use of reflection which in fact it does not because YOU write the bytes down to a stream and YOU read them. So no reflection involved unless you (the dev) happens to mess up :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.