简体   繁体   中英

Passing complex objects as a parameter in Android's Navigation Architecture Component

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
    this@HomeFragment.findNavController().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.

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

To use a ViewModel as a sharedViewModel:

In Kotlin:

class Fragment : Fragment() {

    private val sharedViewModel: YourViewModelClass by activityViewModels()
    ...
}

In Java:

public class Fragment extends Fragment {

    YourViewModelClass sharedViewModel = new ViewModelProvider(requireActivity()).get(YourViewModelClass.class);
    ...
}

To make the navigation process hassle-free it is not recommended to pass complex objects as navArgs unless you really need to. Instead, pass in a reference to the object(a Int id or something lighter). You can then retrieve the object in your sharedViewModel when you are into your next fragment .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM