I have an application that has three fragments: Records, Chart, and PieChart. These fragments are stored in a ViewPager in a single activity and the user can swipe between them. The Records fragment has a table where the user can enter data. When the user enters data, the other two fragments need to be updated because they reference this data. Originally, I wanted the fragments to update as they were swiped to, but since they're swipeable I can't use onPause() and onResume() because onPause() (and thus onResume()) only gets called when a tab is 2+ screens away. So I decided to have the Records fragment send a callback to the Activity to tell the other fragments to update themselves when the Records fragment is updated. I keep all the fragments in memory and this works fine until the app goes into the background. When the app comes out of the background, if the user enters data into the table, the app crashes because all of the variables in the other fragments are null. Why is this happening? I am not using saveinstancestate anywhere in my app. Should I be?
Going by the example from here:
http://developer.android.com/reference/android/app/Fragment.html
You should save all your data that's backing each of your UI components in the Bundle of your onSaveInstance() of your Fragment. Then in the onActivityCreated() callback, restore the data.
Related
I have 3 fragments and a bottom navigation menu to switch between them.
In every fragment i have a recyclerview to display data gathered from FirebaseDatabase.
My question is: Should i use Replace() fragment? As it would result in restarting the fragment and requesting again Firebase SingleEventValue every time the user navigate between fragments
Or i must use Add Show Hide? Or that would lead in memory leaks?
The methods you use to deal with the fragments are mostly not relevant. The Firebase SDK will cache data previously fetched, so if you make a second request for some data, from any point in your app, you will be seeing cached data, and it will even work offline. There are no "leaks" involved here when using single value events.
Adding a listener to a database location is a different matter. Ideally, listeners should be attached when a fragment becomes visible, then detached when not visible.
What is the best practice to notify all the fragments that are in backstack on some change?
I try to use EventBus and every Fragment subscribe to the event, then when change is happening the main activity send post event to all the subscribers.
Only onDestroy I Unsubscribe the destroyed fragment.
I don't like this solution because if there are many fragments in backstack, it can be heavy + lots of listeners simultany.
my application, has infinity drill down, from one fragment you replace to other (and add to backstack) and you can replace again (and add to backstack) and so on..., with no end.
A possible solution is to put some data into shared preferences and read it in fragment onResume.
Or you could put that informations in other parts, like configuration servers or external service
Obviously if these fragments belong all to the same activity you can put informations into your activity, then you can read it from that attached activity.
For remote fetched data
You may put your data into a singleton class only responsible to keep data. Keep in mind that in android a singleton could be destroyed in some limit cases, so when your fragment come back in foreground check if the singleton is empty and eventually repeat your fetch call
You can see an example here
Before I begin, I have read this, this and some more articles online. I am still unable to find a right way to do it.
So I have just one activity in my App, and 6 fragments. First one is a ListFragment which loads a list from a SQLite table. When user taps on a row in this list, I do 2 things:
1) Get an int from that row through a listener, and pass it back to the parent activity which stores it as a class variable using a simple setter method.
2) Replace this ListFragment with another simple Fragment. This new Fragment uses a simple getter() on that class variable to retrieve some information from a different table, and show all the details to the user.
So far so good. Now if I am on this details Fragment, and I change the screen orientation, the activity state is not reloaded (as I am checking if savedInstanceState is null in the onCreate()), but however, the class variables lose their value, and my app crashes.
Basically I am trying to pass data from the ListFragment to the details Fragment. I am doing it through the activity, which is causing a problem. As per Android Documentation:
All Fragment-to-Fragment communication is done through the associated
Activity. Two Fragments should never communicate directly.
There is no specific code which is giving me trouble, so didn't post any.
The onSaveInstanceState and onRestoreInstanceState is only used to save and restore per-instance state of an activity in case your activity is destroyed by OS (for example, to free the memory or in order to recreate it when the device orientation was changed). So you can save your variable in onSaveInstanceState and get them back using onRestoreInstanceState.
For your next question, I think this article will help you. Also answer by Gene for this question will help you.
I have an ViewPager with 4 fragments,
at the last fragment I want to make a validation and save the values that was inserted in the previous fragments.
I tried to override the onSaveInstanceState and save a Bunble with the data but the method not getting a call (only when the screen goes off).
How can I save the data and access it from the parent activity?
I think given your requirements you would be better to pass a model object from the Activity to the each of the fragments in turn. Each of the fragment's would store the data required in the model, and the once the last fragment is complete, it can be validated.
As a side note, I believe onSaveInstanceState is used for storing the current state of a Activity / Fragment so that it can be reinitalised without having to call possible long running background tasks rather than a way to share data.
Let's say I have a list of homogenous items which is likely to be changed in the lifetime of my Activity by user interaction or OS events. The Activity contains a FragmentPager which shows a number of ListFragments.
These fragments share the previously mentioned data but display it in different ways. E.g. they differ in sorting order or display only a subset of the data. Currently each fragment keeps a separate list containing the respective part of the data in the respective order.
When the data changes, basicly every fragment has to be updated. This means resorting or adding/removing items from some of the fragments. What is the best practice to keep the data in the different fragments consistent?
Currently I have some sort of an observer object, which is notified when something changes and subsequently notifies the connected fragments. But there are a couple of problems:
When the app just started, some of the fragments haven't been created by the FragmentPager, so notifying them is impossible.
When swiping through the fragments some of them get paused. In this state, they can't update their list. Should they disconnect from the observer in this case? This leads to:
When a change happens, while a fragment is disconnected, it basicly misses it.
And so on...
If I understood your ViewPager shows the same data (or it's portion) but in different views. So, I belive ViewPager shouldn't act in any way when data is changed, it's responsibility of Adapter.
About points below you said:
a) creating of fragments inside ViewPager can be managed by you. Just see javadoc of ViewPager::setOffscreenPageLimit(int limit) method.
b) I think you should do nothing with UI when data changed but fragment is in paused state. If you want to update do it in onResume(). Or better to set some field in DB (if you have) to "updates present" state and check it when Activity(Fragment) appears.
c) As in previous option - if fragment disconnected just ignore updates. Or if you really interested in that update use sticky BroadcastReciver (be carefull sticky BR is expensive thing)
You can keep your data in Application class, update only visible fragments when data has changed, and always ask for the new data in Fragment's onResume(), that'll do it
What I would do is have each ListFragment use a Loader to load its data. Then, instead of having the observer notify the Fragment (which might have been killed) to refresh its data, register an observer for each Loader so that it will know when the data source has changed, and will re-query when one has been detected. (If your data source is an SQLite database and you are using a ContentProvider, the CursorLoader will do all of this for you).
This is the implementation I would recommend because
Each Fragments behavior remains self-contained (i.e. each is a re-usable component that is not tied to any specific ViewPager or Activity).
It avoids the complexities of having to deal with potentially destroyed Fragments within your ViewPager.
If you need a quick fix, you could probably get away with forcing the Fragments to remain in memory using ViewPager#setOffScreenPageLimit(int limit) as Ivan suggests... however, this isn't as clean of a solution in my opinion.