I have one FragmentActivity that holds ViewPager in which I trough custom FragmentPagerAdapter create 4 child Fragments. Fragments contain ListView in which I show data. I make a call to the web API in FragmentActivity when the response is finished; I want to notify all fragments that data is ready and fill the adapter with that data. Note that data is not available at the time of Fragment initialization.
I could save a reference to fragment when creating it and call “myFragment.hereIsYourData(Object data)”, but the reference get changed after recreating Activity when killing app for low memory. (If I’m not mistaking + I read that keeping a reference to fragment in Activity is a bad thing)
Just for clarification I can’t use “findFragmentById(id)” because ViewPager does’t expose that. I could use the “getFragmentTag” hack explained here https://stackoverflow.com/a/9744146/1025364 but I don’t want to : D.
Then I have tried to ask for data from Fragment onResume method trough interface to Activity (like explained in Developer guide), but the data is not reedy at that moment. Should I ask for data in some sort of a loop until its ready?
What is the best approach in situation like that?
Related
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.
First I explain the structure of my app. To be precise MainActivity of my app contains Viewpager with two fragments. Each fragment contains a listview. The data inside listview is being populated from web service call.
This web service call is actually done in activity not in the fragments through AsyncTaskLoader which loads data for both listviews and saves the response in two Application variables (Lists of data for both listviews). On finish of loader , i just do viewpager.notifydatasetchanged() (which reloads the fragments , thus sets the data in listview).
As the fragments in viewpager are initialiased by viewpager's adapter no by the activity directly, I found this approach better.
Options I had, but rejected (pls correct me If I could utilise them)
Asynctask to call webservice (headache if handling rotations)
IntentService (updation of UI)
Is there any other approach to load data and handles the fragments?
I am trying to design an Android app which:
grabs JSON data from an HTTP link
iterates through the data and forms an ArrayList of my Object.
Now the HomeActivity extends an ActionBarActivity, which implements TabListener.
It has 2 tabs with a Fragment in each. Fragment 1 is going to hold a listView from the JSON data. Fragment 2 is going to show a Google Map with markers based on the same JSON data.
Now, I'm just wondering what is the best approach to use this AsyncTask.
Should I place it in the Activity and then use interfaces to pass that ArrayList to both the Fragments?
Or ... how should I do this? Thanks! Some tips on caching would also help.
Yes, if your fragments are both using the data, it seems reasonable to put your AsyncTask in the activity. If the AsyncTask is a non-static inner class of the Activity, then in your postExecute you can fetch pointers to your fragments from your activity, and then call methods on the fragments to give them the new data (or tell them that you have stored it somewhere -- however you've implemented it).
My app have many fragments, many of them extends from ListFragment and have Adapters for read database (SQLite). My app have only one Activity (for now) and have a Service is binded when the Activity start.
My problem is how to notify to a fragment when are changes in the database from the service or other fragment.
Example of architecture of my application
MainActivity
tabhost
fragment
listfragment
listfragment
fragment
fragment
gridfragment
fragment
MainService
comunication_server
send_data
receive_data
As you can see my application has several fragments, and I want to inform the fragments that the data has changed. or at least launch an internal broadcast can hear by fragments like iNotifyPropertyChange .NET C#/WPF
Think about implementing CursorLoaders. They'll give you the ability to load your lists asynchronously and they will monitor the source of your data for changes and update lists automatically. They're super easy to implement.
Lars Vogal has a neat little tutorial on them.
I have a FragmentActivity with a ViewPager and a FragmentPagerAdapter.
Each fragment in the ViewPager needs to load data from a webservice. I've written the LoaderManager/custom Loaders to perform the webservice calls, parse the XML etc and these are working fine.
There are two possible designs for managing the loading and fragments.
1) The main activity to manages the loading on behalf of the fragments.
The fragment makes a call back to the main FragmentActivity requesting it's data. The activity checks if a loader for that data is already running, if not it creates one and performs the load.
In this case how do I tell the fragment that the data has been loaded and is ready for painting. I am using custom loaders not simplecursors so the fragment will populate its textviews etc from an object instance so it must be told when the objects have been populated.
It doesn't seem that a FragmentPagerAdapter allows you to allocate Tags or Id's to the fragments when you instantiate them in getItem. How can I find the fragment from the FragmentActivity and tell it to paint its data?
2) Each fragment manages its own loading.
The fragment initialises its own Loader, each fragment has its own onCreateLoader/onLoaderFinish etc.
The problem I've come across here is if the user pages off that page whilst the loader is running the fragment seems to be destroyed (sometimes). As a result the onLoaderFinished isn't being called and the fragment can't tell the main activity that it's finished - the main activity is controlling a progress indicator (setProgressBarIndeterminateVisibility) in an actionbar.
So what is the best design pattern when you have a FragmentPagerAdapter, a ViewPager with several pages which need their own loaders?
Another issue I've encountered is using getSupportLoaderManager to check if any loaders are running. I call this from the onLoadFinished. If no loaders running I can hide the progress indicator. However the hasRunningLoaders sometimes returns true, even though all the loaders have completed.
LoaderManager lm = getSupportLoaderManager();
// If the loader is not already running, start it...
if (! lm.hasRunningLoaders()) {
setProgressBarIndeterminateVisibility(Boolean.FALSE);
}
Many thanks for any advice or pointing me in the direction of some decent samples.
Ta
Martin.
With regard to design 1, I did something like this by maintaining a reference variable to the fragments that I create. In your FragmentPagerAdapter's getItem method, you can assign the created fragment to that variable before returning it. This will allow access to your fragments from the main activity.
I'm not sure if this is entirely correct, but I remember that when your Fragment is destroyed while a Loader is getting data in the background, the onLoaderReset method is called (instead of the onLoaderFinished). This would allow you to reset your progress indicator appropriately.