Proper use of instance state save from fragments - java

I have a fragment an onCreateView I do a background asyn call to fetch some data to display.
I save the object that is the result of the background call as a member variable of the fragment.
When I see the UI and the data all is ok and when I press the home button the app goes to background. When I reopen the app the data are in the UI inflated by my fragment.
So now I am not sure about the following:
1) Should I save my object in the onSaveInstanceState?
2) Where should I expect to restore it? In the bundle passed in the onCreateView?
3) When would the data be saved in my bundle? I mean how can I see that without saving it right now, I would lose the data from my UI?

In your fragment constructor, add the following line:
setRetainInstance(true);
Control whether a fragment instance is retained across Activity
re-creation (such as from a configuration change).

Also like an activity, you can retain the state of a fragment using a
Bundle, in case the activity's process is killed and you need to
restore the fragment state when the activity is recreated. You can
save the state during the fragment's onSaveInstanceState() callback
and restore it during either onCreate(), onCreateView(), or
onActivityCreated(). For more information about saving state, see the
Activities document.
It seems that your fragment works alright by fetching the data every time it's shown.
If you want to save its data to avoid the async fetching you can use onSaveInstanceState, which requires you to save your data using a Bundle.
Then in onCreateView check if savedInstanceState is null and if it is do the async fetch, otherwise recreate you data from the savedInstanceState Bundle.

It depends on your data (size).
Do you want to have a fragment on the back stack (with retained fragment you cannot)?
For "home button action" and non-retained Fragment implement Parcelable interface and use onSaveInstanceState() to save and onCreateView()/onCreate() to restore data from a Bundle.
Be aware for back button you will loose your data.

Related

Question about BottomNavigationView in Android

I have general questions about BottomNavigationView. I would like to have a BottomNavigationView in each of my Activities in an App for ordering something (e.g. food). It should have 4 buttoms:
Back
Info
Stats
My Orders
With 'Back' the app should just go back to the previous activity. The buttoms 'Stats' and 'My Orders' should switch to a persistent activity that should not be destroyed when not being displayed. 'My Orders' should display the last orders. The buttom 'Info' should only display some information about the current item or current menu (depending from which activity it is called). So basically I have 2 questions:
Should the Activities 'Info', 'Stats', and 'My Orders' be real Activities or just Fragments? Normally I think that at leat 'Stats', and 'My Orders' should be real Activities as they are persistent. But in many BottomNavigationView only Fragments are used?
How can I pass content information to the Activity/Fragment 'Info'. This Activity/Fragment should display information based on the Activity is was called from. Let's say the Activities are different dishes. Do I have to create a separate Info-Activity/Fragment for each dish? Or can I somehow define a dynamic Activity/Fragment that displayes information based on the current Activity?
I'd appreciate every comment and I'd really appreciate your help.
The recommended approach is Single Activity and Multiple fragments.
You can do this using Jetpack's Navigation Component
In case you need to pass data from an Activity/Fragment to the new calling Fragment, it can be done by setting arguments on the calling fragment and then getting it on the called fragment. If there is something which requires to be dynamic, for example- dishes fragment, make a single fragment and common layout and load the data dynamically from the backend.
For Setting Arguments, this should help
How to pass a variable from Activity to Fragment, and pass it back?
Note: You can use fragment without using Navigation Components but you have to use FragmentManager and FragmentTransaction and also have to maintain the Backstack by yourself which could be quite complicated

View Model can be heavy if we use single activity based architecture

As google suggests single activity based application. I have a situation where I need clarification.
I have one activity containing 3 fragments and each fragment is linked to different fragments having other screens.
should I use only one viewmodel for each tabs or should I use one view model having different screens.
As I have only one activity and view Model resides till activity is destroyed. Do all viewModel that I will create for each screen will contain data until activity is destroyed. If this is the case will it make my app heavy.
should I use only one viewmodel for each tabs or should I use one view model having different screens.
You should use the smallest scope for each ViewModel possible. Generally, this means that data associated with only one fragment should use a ViewModel associated with just that one fragment.
As I have only one activity and view Model resides till activity is destroyed. Do all viewModel that I will create for each screen will contain data until activity is destroyed. If this is the case will it make my app heavy.
ViewModels live only as long as the ViewModelStore they're attached to is around. Therefore if you have a ViewModel associated with a fragment, it'll survive only as long as that fragment exists. For example, if that fragment gets popped off the back or you call remove(), then the ViewModel is destroyed. ViewModels only live as long as your activity if you specifically create them using the activity as the ViewModelStoreOwner (for example, by using ViewModelProvider(activity)).
should I use only one viewmodel for each tabs or should I use one view
model having different screens?
In single activity architecture, you can use ViewModel with either Activity Scope or with Fragment scope. As #ianhanniballake suggested in his answer, use ViewModel with Fragment which means that each tab in your case will have its own ViewModel attached to fragment. This will provide separation of concerns for functionality in each tab.
As I have only one activity and view Model resides till activity is
destroyed. Do all viewModel that I will create for each screen will
contain data until activity is destroyed. If this is the case will it
make my app heavy.
Again answer to this question is connected to your first question and explanation provided to it.
While working with single activity architecture narrow down scope as much as possible. Use ViewModel associated with activity (lets call it Global ViewModel) only to keep data which is used across multiple fragments. This way viewmodel will contain data till activity is in backstack and fragments can use it when required.
This will provide you 2 advantages
ViewModel will not contain additional data which you mentioned in your 2nd question
And most importantly there will not be any data discrepancies while accessing fragment multiple times. For e.g. if you open any fragment, fetch data from api and keep it in Global ViewModel and fail to delete it when remove fragment from backstack then your fragment will show old/obsolete data when you will open fragment next time. To avoid this scenario it is better to keep this data in viewmodel with fragment scope.
I hope this will be useful.
Each Fragment usually should have a ViewModel itself but in some cases where you want to share the same ViewModel instance, you can achieve it with having a ViewModel scoped to an activity.
ViewModel objects are scoped to the Lifecycle passed to the ViewModelProvider when getting the ViewModel it is found on the docs and you might have to read other details there but technically you can scope a ViewModel to a Fragment or to an Activity.

Getting data from Firebase to Fragment

I have one Tabbed Activity which has 3 Fragments. In every Fragment I need to display some data from my realtime firebase database. So i thought that instead of connecting to database in every Fragment, I would retrieve data to static variable inside my tabbed activity, and then display this data calling this variable from every Fragment. But when I try to set my text to data from Firebase it shows an error, because in that moment, my static variable is "null". How do I make sure that that first I retrieve the data to my variable and then set my text. Because right now, my tabbed activity has ValueEventListener inside the onCreate method, and I am trying to set the TextView, from OnCreateView inside Fragments. I made some tests and realized, that the onCreate method inside of Tabbed Activity is called first, but onCreateView from my Fragment is called shortly after when the data isn't retrieved yet.
You can simply get Reference of your fragments like below
ExampleFragment myFragment = (ExampleFragment)getSupportFragmentManager().findFragmentByTag(ExampleFragment.TAG);
And then when the data finished loading. Send it like below code.
myfragment.sendDataToFragment(myData);
i hope it helps.
Also you can use interfaces to communicate between Activity and fragments.

Retain Fragment data without retaining Fragment

I'm working on an app, which needs to do a lots of calculations in the background.
To do this I start a thread from a fragment and pass a Handler to it, so it can pass messages back. When a configuration change occurs (screen rotation, ...) Android recreates the Activity and Fragment. I'm looking for a way to get a reference to the thread from the new Fragment, so I can pass a new Handler to it.
Retaining the whole Fragment is not an option for me, as it uses a different layout in landscape mode.
I can't use onRetainNonConfigurationInstance(), as that is only supported on Activities, but my thread lives in a fragment.
I can't use onSaveInstanceState(), as that only supports primitive data types.
Are there any other ways for me to retain a reference to my background thread when the fragment is recreated?
I solved the problem using a headless retained fragment, which manages the thread.
My normal fragment creates a new instance of the headless fragment, which is retained. The headless fragment has start() and stop() methods, which in turn start and stop the thread and a setHandler() method, which changes the handler messages are passed to.
Whenever the non retained fragment is recreated it just has to get the retained fragment from the FragmentManager and call setHandler() with a new Handler.
You can use the ViewModel of android architecture components to do this.
Lifecycle aware components: https://developer.android.com/topic/libraries/architecture/lifecycle
Google's ViewModel: https://developer.android.com/topic/libraries/architecture/viewmodel

Android: Method in Fragment Lifecycle

I'm looking for a method in the Fragment Lifecycle, but I'm not sure which one.
Here's my situation: I've got a Fragment inside a ViewPager. The Fragment displays a List with some information. I fill the list in the Fragment's onCreateView(). When the user opens a different Activity (settings in this case) and changes some settings, the information that the List in the Fragment has to show, changes. When the user returns to the Fragment using the Back-button, the onCreateView() isn't re-called, so the information in the List isn't updated.
My question is: The onCreateView()-method isn't called when the user returns to the fragment form a different Activity, but which method is called here? I need to know this because then I can fill the List in that method.
Thanks in advance!
Important and non-obvious point it that Fragment's onCreateView() being called not only in case you selected Tab with this Fragment. So don't rely on onCreateView() of Fragment when using ViewPager(). When You select the Tab, Android creates sible views (caches them) or makes something similar.
You should call your update method when user selects proper Tab in ViewPager (don't remember exactly, but hope it helps).
onResume() is the simple answer, called when user comes back. for more details refer lifecycle here FragmentLifecycle

Categories