I have FragmentA and FragmentAViewModel. FragmentAViewModel calls RepositoryA for getContactData(), which is a silent call without blocking the UI.
Now User Navigate to FragmentB which has FragmentBViewModel. User needs the getContactData() response here in FragmentB.
I want to show loading progress in FragmentB until the data is available in RepositoryA. Once Data is avaiable in RepositoryA, i want to update the FragmentBViewModel and FragmentB .
How can do that.
NOTE:
I dont want to share the ViewModel since this is the small use case in whole feature.
I cannot use LiveData in Repository since i need Lifecycle owner to observe that.
Do i need to use RXAndroid or Broadcast for this ?
The flow is a bit strange to me but it's totally doable.
If both pages are reference to the same data source, ex: same database via Room interface.
Then first page can trigger the fetch or update while the second page subscribe for the update and display it.
I am following the one-single-activity app pattern advised by Google, so if I want to share data between Fragments I have to share a ViewModel whose owner must be the parent Activity. So, the problem becomes because I want to share data between only two Fragments, independently from the others.
Imagine I have MainFragment, CreateItemFragment and ScanDetailFragment. So, from first one I navigate to CreateItemFragment in which whenever I press a button I navigate to ScanDetailFragment in order to scan a barcode and, in consequence, through a LiveData object inside the ViewModel I can get the scanned value back into the CreateItemFragment once ScandDetailFragment finishes. The problem becomes when I decide to cancel the creation of the item: I go back to the `MainFragment' and because the ViewModel's owner was the Activity's lifecycle, once I go again into CreateItemFragment, the previously scanned value is still there.
Any idea to reset that ViewModel?
but, aren't Viewmodels also aimed to share data between different views?
No. Each viewmodel should be responsible for one view. The "shared viewmodel" pattern is for cases when you have one large view (i.e., activity) that has multiple subviews (i.e., fragments) that need to share data / state, like the master / detail example in the documentation. It's a convenience for these cases when you need real-time updates amongst the subviews.
In your case, you're navigating between fragments and as such should be passing data through the transitions. This means passing arguments along when starting new fragments and registering for results when they complete their task.
Then each of your fragments is isolated, self-contained, more easily testable and you won't end up with a God-ViewModel that does All The Thingsā¢ and turns into a giant mess as you try to jump through hoops accounting for every state it could possibly be in.
You can use callbacks in such cases to share data between fragments. or if you use DB/Sharedpreference/Content provider then you do not have to worry about sharing data each page will fetch its own data from the store(DB/SharedPreference/Contentprovider).
you can also try https://medium.com/#lucasnrb/advanced-viewmodels-part-iii-share-a-viewmodel-between-fragments-59c014a3646 if this guide helps
You can clear LiveData value every time when you go into CreateItemFragment from MainFragment.
Or you can just clear it from the CreateItemFragment in onBackPressed() method.
When you cancel the creation of item,set livedata value to null.then within observer code if(updatedvalue!=null) write your code using updated live data value.in this way you can avoid last updated value.
At the moment (on 2022), the method viewmodel.getViewModelStore.clear(); or onCleared(); is deprecated.
So, if you want to clear data holded by ViewModel or clear value of LiveData, you just need use 1 line code like this:
mainViewModel.getLiveData().getValue().clear();
getLiveData() is my method inside MainViewModel class to return liveData variable
getValue() is defaut method provided by LiveData (MutableLiveData: setValue(), postValue())
If you need to clear data when user press on Back button in Fragment, you can do like the code below & put it inside the onViewCreated method - the method of LifecycleFragment.
private void handleOnBackPressed() {
requireActivity().getOnBackPressedDispatcher().addCallback(new OnBackPressedCallback(true) {
#Override
public void handleOnBackPressed() {
Objects.requireNonNull(mainViewModel.getLiveData().getValue()).clear();
requireActivity().finish();
}
});
}
My project on Git if you want to refer code (it still updated): https://github.com/Nghien-Nghien/PokeAPI-Java/blob/master/app/src/main/java/com/example/pokemonapi/fragment/MainFragment.java
I disagree with #dominicoder. At this link, you can find a Codelab made by the Google team updated to Oct 30, 2021. The shared ViewModel pattern can be used when you need a coherent flow to achieve a specific task inside your app.
This method is useful and a good practice because:
The Jetpack team says that has never been a recommended pattern to pass Parcelables. That's because we want to have a single source of truth.
Multiple activities have been heavily discouraged for several years by now (to see more). So even though you're not using Jetpack compose, you still should use a shared ViewModel along with fragments to keep a single source of truth.
Downside:
You need to reset all the data manually. Forgetting to do so will bring bugs into your app, and most of the time, they're difficult to spot.
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.
Hello Everyone,
I had do a search on activity retain state.So I am getting to options/solutions for that:-
Using savedInstanceState() and retainInstanceState() methods.
Using Parent Child hierarchy declare in manifest and its working.
(Example Url:-How can I return to a parent activity correctly? I had same problem mention in Note scetion of the answer.That case is match with my problem.
So,I want to retain the activity states just like Whats app(call/chats/contacts fragments).
But In My scenario,I am fetching the contacts from server.So how can I persist my data while switching between chat and my fragment activity?
So while timing to fetch new data of my contact list from server.I want to save ui of my listview/recyclerview with old data previously I had.
Also suggest which method is good from above two methods or need to implement in other way.
I am working on a Android app which have 5 fragments and some java classes.
I have to be able to read and edit an arraylist containing pojo's from across these fragments and classes. For example updating from the internet and then updating recyclerView in one of the fragments or sorting the objects in a recyclerView in one fragment and have those changes updated in the recyclerView in another fragment.
I have been looking at notifyDatasetChanged, but cannot get it right, when starting an update in the background and then wants it to update onSucceed in the active fragment.
I have been looking on RxJava with the Arraylist as observable, but once again I ran into problems when I wanted to subscribe from multiple fragments.
And of course I did a arraylist in a singleton, but I am pretty sure that is bad coding :-)
I would put the data that is going to be accessed by all of the fragments in a Service. Each Fragment can bind to the service to retrieve a reference to the data and to register a listener (you will have to make a custom one to handle the events that you are interested in) that will tell each Fragment to update its own view. Each Fragment would implement its own Adapter that would wrap the shared data that lives in the Service.