I have one Activity and want to share data between that Activity and fragments. I put data in extra while in a fragment and put also other data in it. That way I Have a shared Bundle across my application. I only see examples of passing a Bundle to an Intent but its also possible to change that data while in another fragment. This does not break with the self-containment of fragments. I dont put them in some method in activity because then you will have to cast the activity. Can anybody tell me its right to do? I know about shared pref but I dont want a file based solution. I know about passing parameters with newInStance but I also need to save data back in fragments. passing parameters is only forward not shared.
Passing data from activity/fragment back & forth using Bundles would have some limitations and issues for instance:
To pass a complex object, you'd need to use a serializable marked with a key.
Keeping shared keys in different parts can lead to runtime
errors or data loss if keys are wrong.
Serialized objects are not recommended, but it can be solved with Parcelables. But maintaining that is not that easy for complex objects check here, and you would need to customize that for different types of objects.
Still you don't share data among different fragments but they're just transported; and need to be transported over and over again when you go to a new part of your app.
Not guaranteed to keep the data if the activity is recreated.
Instead of that you'd use ViewModels through MVVM structure where:
Data can be shared on different levels of lifecycle scopes; this means that if the ViewModel is instantiated through ViewModelProvider in activity; then it can be accessed in any part of the activity, or underlying fragments. And if you want to keep only data shared between any fragment and its underlying fragments; you'd bound the ViewModel instantiation to that fragment instead.
ViewModel is instantiated once in the owner, and accessed in the subordinate fragments with no re-instantiation.
If the activity is re-created, it receives the same ViewModel instance that was created by the owner, and the view's data won't be lost.
When the owner activity/fragment is finished, the framework automatically calls the ViewModel's onCleared() method so that it can clean up the resources.
Here is a code lab that you'd check.
Related
I am struggling on the guidance around how we manage objects with global scope when using fragments. I have a user/auth object and need it in almost every fragment as it is required for calling webapis. Normally I try to use ViewModels but that seems like a 1 per fragment (or shared by multiple fragments as in master/detail) is the recommendation. So the idea that a AuthViewModel is instantiated in each fragment does not seem right. I could keep a user object in the fragment and use it for authentication or use it in an extended Application object.
Create a AuthViewModel with a authobject and use it in each fragment
Store the authobject in the MainActivity and use it in each fragment as required
Extend the Application object so it can be used by each fragment
Or is there another option?
I need the object in case a call fails so that I can refresh the access token or even the user if their token has expired.
If it is simply Auth related object, I usually prefer to keep it below the business layer, ViewModel and Fragments/Activities should have no reason to deal with low-level objects. Preferably this object should exist on the network layer or even better an Auth Layer that sits on top of the network layer, which can be a singleton and be shared among those that need it.
I have an application that needs to collect some data before doing it's main job.
So, the first fragment collects data, the second fragment collects data and then the third fragment uses the data.
The problem is: data in the first fragment is uncorrelated to the data I collect in the second fragment.
How can I pass the data from the first fragment to the third? Should I incrementally pass all the data I collect in the next fragment arguments, or should I store them elsewhere? I'd really like to know what the best practice is.
explicative image
I won't use a database, since I don't need to permanently store the data.
Thank you!
As for any best practices, the best answer is "it depends".
If your app is really small and simple then it's okay to pass data from one screen to another in the arguments bundle.
A more complex approach is to store data somewhere outside of these Fragment lifecycles.
It's a general rule you can implement as you want. A couple of examples below:
Data can be stored on Application class level. Application class runs for all application lifecycle. Every fragment can get Application instance from its activity like activity?.getApplication().
Data can be stored on Activity level if all fragments run in a single activity. Activity can be obtained from Fragment using activity or requireActivity().
Data can be stored on a parent fragment level if all fragments run in this parent fragment. It can be obtained from child fragments using parentFragment.
All these approaches suppose you to cast some "parent" thing to specific interface or implementation. For example, if your fragments run in the MainActivity which is stores some val data: Data as its property, then you should use it something like this: (requireActivity() as MainActivity).data
Clarification on a couple of popular answers:
The Shared ViewModel is just a special case of activity-level approach as ViewModel instance is stored in Activity scope.
The SharedPrefrences like any "database" approach is a special case of application-level approach as SharedPreferences is stored on application-level (in the file storage) no matter where you create its instance. (And SharedPreferences persists data across application starts, so it's not your option)
In addition to mentioned "Shared ViewModel" technique, Androidx introduced new "Fragment result Api" starting with "Fragment" library v1.3.0-alph04 (currently in beta) which could be used for communication between pair of Fragments or Activity-Fragment.
A Fragment/Activity set a listener in FragmentManager by specifying a key and other Fragment/Activity send data (in form of a Bundle) to the listener with that key. If there's no listener for the key, FragmentManager keeps newest data until a listener gets registered.
Pay attention that listener and result must be set on same FragmentManager instance.
I my opinion, its good for signals (events), not for sharing data. A situation I found it useful, was sending "onWindowFocusChanged" from Activity to Fragment. In case of sharing data, Shared ViewModel is better.
TL;DR
How do I deal with Activities that actively change data (for example through an EditText)? Do I keep saving their state in the SavedInstanceState on rotation and only use the ViewModel when all of the fields are ready, or is there a way to make the ViewModel responsible for checking/holding/using the UI's data?
Question
I'm developing my application using Google's Arch. Components, and writing my latest class I've noticed I'm not really sure on what the best practice is when handling, say, data coming from an Activity form.
Example
I have a POJO made of title, description, location, type
I have an Activity with four EditText: title_et, description_et, location_et, type_et.
My ViewModel, through a Repository (irrelevant here), can send an object to the Database when the sendObject function is called.
How I'm doing it now
The activity has the mTitle, mDescription, mLocation, mType.
On rotation, the activity saves all of the EditText values in the savedInstanceState bundle, and it loads them again populating the views.
When the user wants to send the object, it clicks a button and the activity calls the function viewModel.sendObject(mTitle, mDescription, mLocation, mType) after the necessary checks.
Problems with this approach
The activity is responsible of holding/checking all the data of the EditTexts, basically making the ViewModel only responsible of interacting with the Repository.
What I'd like to accomplish
Ideally, I'd want to make the Activity only responsible of the UI, delegating everything to the ViewModel.
This way I could call sendObject() and the ViewModel would have already all of the data needed.
The LiveData situation
Right now the ViewModel has only one instance of LiveData, inside that there is a Resource (which is taken from here) and it's used to "tell" the Activity that new data has arrived or an error occurred.
This approach works fine in all Activities that just receive data from the network and display them. What do I do when I want to synchronise data coming FROM the Activity? Do I use one LiveData for each field and use that to display potential errors?
I've read most of the samples but all of the Activities there are passive.
Conclusion
Thanks to anyone who takes the time to help.
You can either separate the logic into a model string class with another class containing all your String values for the edit text fields are just assign the String values at the top of your class.
You can have a LiveData of your model in the ViewModel and alter it from the View (Activity/UI). The downside is that to update the LiveData, you need to copy whole Model, edit it and post it back to live data.
The second way is to dissect Model's components in the ViewModel into individual parameter LiveDatas. Later when form is submitted you can reconstruct the Model.
What you can do for native fields is use data binding. For other you need manually update LiveData from the View with listeners etc.
I'm developing an android application in which is needed one object which contains an arraylist of objects to be edited.
The idea is each of the objects in the arraylist is to be edited in a different fragment.
The app is using ORM to sore objects in database, so I need each object from this arraylist to be edited in different fragments and when save button is pressed to collect the objects from the fragments, to update the main object and save it.
Now I'm creating the fragments and using setters I'm setting the objects for each fragment in the main activity:
DailyFragment fragment2 = new DailyFragment();
fragment2.setdaySchedule(daySchedule);
fragment2.setmDayIndex(1);
Using this approach in each fragment I have reference to the needed element of the arraylist, so it is not needed anything to be done when the main object is saved.
I need advise if there are better ways this to be achieved?
I would consider moving your data out of the fragments.
Look into using something like dependency injection (dagger) to create places to keep and modify your data.
Ideally your fragments contain little more than what is needed to display information correctly. If you bundle everything into activities/fragments you'll continuously be hampered by the need to communicate between them.
I've got a pretty complicated layout: a master-detail view, where the detail fragment contains a pager with 3 other fragments in it. I'm getting a bit lost ensuring that each fragment has the correct arguments passed to it, especially since some data is loaded via async tasks in the main detail fragment and is then pushed into subfragments.
Add to all of this that many fragments are displayed in activities on phones, and I've got lots of different ways of loading the same fragments.
What would be a good way of ensuring that all of the arguments I need are set for each fragment, by every activity/fragment that uses them?
I was thinking of adding a static 'build' method to the fragments that would return an instance of the fragment with the arguments bundle correctly populated, but this doesn't really work when data is set following asyncTasks.
Can anyone suggest how I can manage the data flow between my fragments & activities?
Taking the static 'build' idea one step further, you could create POJOs that hold the necessary arguments for a given fragment and add methods that can do the POJO <-> Intent conversions.
The master view could create a POJO, popuplate its fields, convert POJO to Intent and pass the intent to the fragments. The fragments then could build their own POJO from the intent.