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.
Related
I'm a newbie to android app dev and I have one little problem. I'm developing a note app with a single activity(MainActivity) which has a NavHost and host three fragments(NewNoteFragment, NoteListFragment and NoteUpdateFragment). The note details are stored in a sqlite database.
Whenever user creates new note or update the existing note, the changes can only be stored to the database in NewNoteFragment or NoteUpdateFragment's onPause override i.e when the user navigates from the said fragments to the NoteListFragment. Although the changes were saved but the recyclerView in the NoteListFragment doesn't update immediately until i kinda navigate away from the NoteListFragment to other fragment.
When I check the logcat i noticed the onResume override(which is where I called my adapter.notifydatasetchanged() method) in my NoteListFragment is called before onPause override of the replaced fragments.
Is there a way to change the fragments navigating behaviour where onpause is called before the onresume? Or any better way to achieve my said aim.
You cannot set onPause to be called before. Since you are using fragments try to update the recyclerView onViewCreated() function of Fragment.
You cannot change the Android Lifecycle, it is always set.
I would advise using Room and LiveData, any changes in the database would automatically update the RecyclerView you mentioned.
You can read more about live data here
Let me just say up front that this is more of a "structural" question, and I'm not asking anybody to write code; I'm just trying to figure out how I should be structuring my application.
I'm using Android's DrawerLayout/NavigationView for my app. This means that MainActivity is the host for all my fragments.
I currently have three fragments (in reality it's many more, but they are more or less exactly like these three fragments, just for different sets of data).
ListFragment
DetailFragment
EditFragment (used for both adding and editing)
On my ListFragment I have (surprise!) a list of items. This is a LiveData collection on SharedViewModel (which is tied to MainActivity's lifecycle). When an item is tapped I pass the event through to MainActivity by means of an interface listener.
MainActivity then loads up the DetailFragment. In the same call, I load an instance of SharedViewModel (again tied to MainActivity). I set SharedViewModel.selectedItem to be the tapped item. Then, in DetailFragment's onCreate function, I get the selected item via ViewModelProviders.of(activity).get(SharedViewModel::class.java).selectedItem.
On the DetailFragment, there's an edit button. This goes through more or less the same routine described above, but loading up the EditFragment instead. When the edited/added item is saved, I add/replace the item in SharedViewModel's collection through MainActivity's interface listener.
Obviously this is not optimal for several reasons. For one, it means that I've got at least five large sets of data hanging around for MainActivity's lifecycle (the entire lifecycle of the app, essentially). Also, MainActivity grows way out of hand as I have to add more and more functions to handle events.
What I want to do is have, for example, my list of items on a ListFragmentViewModel which is tied to ListFragment's lifecycle. My selected item on a DetailFragmentViewModel, my editing item on an EditFragmentViewModel, etc.
My problem is that I'm not sure how to properly pass the data around in this case. For example, let's say I add a new item in EditFragment. How do I get that into ListFragmentViewModel's collection of items? ListFragment is in the back-stack, so its viewmodel hangs around and doesn't reload the data when it's navigated back to, since it still has the collection from before. This makes sense and is probably how it should be (after all, who wants to wait for all the data to load when they go to DetailFragment and back to ListFragment?), but it means that I don't get my new item in the collection.
That's just one example, but I'm running into quite a few issues like it (e.g. passing the selected item to DetailFragmentViewModel.)
I'm not quite sure what direction I should even be going here. Can someone more experienced help me out?
let's say I add a new item in EditFragment. How do I get that into ListFragmentViewModel's collection of items?
EditFragment tells your item repository, "yo! here's a new item!". The item repository arranges to update your backing store, plus emits an event to interested parties notifying about the data change (e.g., emits an event on an RxJava PublishSubject). The ListFragmentViewModel listens for those events and reacts accordingly.
ListFragment is in the back-stack, so its viewmodel hangs around and doesn't reload the data when it's navigated back to, since it still has the collection from before
It should be finding out about the data change from your item repository, and doing whatever makes sense to reflect that data change. That could be simply taking data from the data-change event and updating its in-memory content. That could be re-requesting information from the backing store. In principle, it could be something else.
I have a main activity in which i have used a view pager.So i can move between 4 tabs and the view pager handles all of that.One of my tabs scans the contacts on the phone to get their details and display it in the recylerview which is in the same tab,this task takes a long time and i am doing this in an async task.Everything is working fine but the problem is if i move to another tab while this scanning is going on the data does not get applied to the recycler view possible because that fragment is being destroyed.
Is there a workaround for this or should i just prevent the user from shifting tabs while the scanning is going on (if so some sort of code or a link to the code would be really helpfull).
I wouldn't recommend you force a user to stay on a page whilst data loads. This sounds like it would only frustrate people. To that end, I have a couple of ideas that should keep your AsyncTask running whilst your Fragment isn't visible.
First, you could call setOffScreenPageLimit(2) on your ViewPager. As you only have four Fragments, this should mean all of them are stored in memory.
viewPager.setOffScreenPageLimit(2);
Another approach is you may be able to create a UI-less Fragment whose sole function is to conduct your AsyncTask and then, once it reaches onPostExecute(), pass the Cursor result to the Fragment that requires it with either an interface or an EventBus of some sort (i.e. LocalBroadcastManager or one of the other many excellent libraries that exist, such as GreenRobot's EventBus).
Edit If you like information on how to create such a "worker" Fragment, then there is a very good and detailed post on androiddesignpatterns.
I have a Fragment that shows some data saved in android Preference.
when these data changes due to various operations of the app, I want to update the fragment with the new data.
I have tried to use
myFragmentTransaction.notifyAll();
But unfortunately doesn't work and the fragment is updated only when I reopen my whole Activity
Houw could I refresh the Fragment on data changes?
notifyAll pertains to threading. Completely unrelated to what you are doing. Instead of trying to tell each fragment to redraw themselves have you tried OnSharedPreferenceChangeListener. Each fragment should listen to changes in the information they care for.
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.