Update Fragment after Async Task completes - android

I have a viewpager fragment setup, and I have an AsyncTask run at the start of the activity to load some data from a JSON get (the data it pulls only needs to be done once, and all fragments will use it). The problem I'm having is I want to take this data from the AsyncTask, and have it be the data that all of the textviews pull from in the fragments.
What would be the best way to load data from an AsyncTask and then have the fragments reference that data. You cant just have the views reference the data from the get-go as it Nullpointers, and I'd really like to avoid having to reference every single textview in the OnPostExecute of the AsyncTask if I had it in the Parent Activity.

Just decouple the storage of the data from the view(s). In that way it will be easyer to handle the download of the data and to show it in the various fragments once the get is completed.
The basic idea is: launch the download in a background thread decoupled from the activity (service or intent service), then update the data a storage (sqllite if it is complex, shared preferences or even a singleton object, even though I don't like the latter approach).
Once the get is performed, inform the fragments that the data is available. Still, you have a lot of options here. If you decided to host the thread / asynctask inside a service, you can bind a callback to it and then notify all the fragments interested, you can use a (local) broadcast message that you can intercept using local broadcast receivers, or you can even use a message bus such as otto.
Finally, I really recommend to use an intent service to perform the async job. It's the easiest way to do any one shot operations without having to deal with service creation and/or with activity configuration changes.

Not sure if this is the "best" way, but what comes to my mind first is the famous Observer pattern.
Create an interface called Observer with a single method called
notify()
Implement this interface in all Fragments that want to be notified when the data is ready
Hold a List of Observer objects inside your AsyncTask
In onPostExecute(), traverse this List and call notify() on every Observer, passing the appropriate data
React on the notification inside your Fragments given the data
This a very rough description of this solution, but I hope you get the idea.

Related

Is it possible to call make network call for fragment from an activity

I am following MVP approach, and I have an activity containing 4 fragments. On App launch activity makes network call, and passes data to fragments.
In one of fragment i have recycler view, and it has Swipe to Refresh.
When user swipe to refresh i need to make network call from fragment, which i learnt is not a good idea, and also i am following MVP approach, so activity has the list of methods to execute in presenter, and presenter decided whether to grab data from local repository or remote.
So what should i do to perform swipe to refresh operation from a fragment.
Should i call activity method, and perform network call from there, and re pass the updated data to fragment, or is it fine to make a network call from fragment.
The answer is neither the Activity nor the fragment. The fragments and activities shall never "fetch" or "handle" data. Only display data. And deal with activity lifecycle with regards to this.
If you want to know further this will be a good read for you.

Activity (Loader - downloading) + 3 Fragments (with Loaders - computation)

I have to download "raw" data from the server every N seconds and then deliver it to fragments (which are inside ViewPager). Every fragment has to do its own computation on the data and then populate own RecyclerView.
I have one idea how to achieve that:
Activity's AsyncTaskLoader downloads data, store it and then
broadcasts an Intent that new data has come.
Every fragment has its own AsyncTaskLoader and connected broadcast receiver.
When fragment's Loader receives information about new data, get it form the activity, compute and deliver to RecyclerView
Is it the proper solution? Is there simpler approach to my problem?
You can use a Service which contain a Runnable code to download "raw" data from the server every N seconds.
Save received data in Singleton, and you can compute and deliver directly to your RecyclerView's adapter
AsyncTaskLoader in the Activity is a good idea, but I would simplify communication with the fragments by using a message bus instead of broadcasting intents.

Does Android development have any concept analogous to WPF data binding?

I have a List of items that I want in a ListView, and I can make it work with setting a custom adapter every time the List grows, but the program flow is kind of weird and I have problems with persistence. (If I switch tabs, the UI gets rebuilt with an empty ListView.)
Now, in my day job I'm a C# developer, so when I look at this problem I see a WPF ListView bound to an ObservableCollection. Does Android/Java have something like that, a "fire and forget" connection between a UI element and a data structure?
You don't need to replace the adapter every time you change the data. The adapter "adapts" between data and view. There is no need to change the adapter as long as the way it adapts does not change.
Activity / Fragment lifecycle is not necessarily the lifecycle of your data collection. You can for example make a singleton data collection somewhere and use an adapter to display that collection all the time. Call .notifyDataSetChanged() on the adapter if you changed the data.
A persistent data collection in Android is probably best backed by a database. Take a look at LoaderManager & ContentProvider to provide and load data then displayed via CursorAdapter.
There is no automatic way of keeping a bunch of data available outside of your Activity / Fragment / .. lifecycle and it can get quite complicated but that's basically what you have to do if you want to keep data for longer than a given lifecycle. Singletons, Activity#onSaveInstanceState(), Activity#getLastNonConfigurationInstance(), Fragment#setRetainInstance(), ... are useful utilities to keep data in memory, databases are good for persistent data.
You have to do a little bit work yourself but it's possible. Use a ContentProvider as your DataSource. How the data is stored is up to you. I would prefer a SQLite-DB. A content provider has the possibility to add ContentObservers. (See this related question.)
You can write a CourserAdapter to fetch the Data from your content provider. And your ContentObserver should call notifyDataSetChanged() on your adapter. This closes the circle and your UI refreshes itself.
In Addition to zapls answer:
You can also write an adapter which contains a BroadcastReceiver. When your DataSource changes you can send a LocalBroadcast. The broadcast handler just calls notifyDataSetChanged() of your adapter. I think this would work around most of the lifecycle problems because only active elements will get the broadcast.
The google documentation has an example for such a solution.

Asynctask in Fragment or Parent Activity?

I have a scenario and I an not sure on what path to go.
Scenario
The app has a Home activity which displays various fragments. The data in the fragments can come either from the web or a local database and is retrieved using an asynctask.
From what I saw, I have 2 alternatives:
Put the Asynctask in parent activity and then use fragment.newInstance(parameters) to pass the result to the fragment. However, if in my asynctask I need to update the progress or some info on the fragment, each time I will have to call newInstance with the new set of parameters.
Add the fragment and put the asynctask in it, in this way when progress is needed, I can update the fragment's views, as I have access to them + when the asynctask is done, I can populate the list with the info.
What would be the correct approach ?
LE: actually for point 1 in order to update the fragment I can call fragment's public methods after I find it with findFragmentById in the parent activity
A better way if you have multiple tasks would be to use an IntentService :
http://mobile.tutsplus.com/tutorials/android/android-fundamentals-intentservice-basics/
You would have a better control to what you're requesting and what you want to cancel.
I would go with the second approach.
My primary reason though would be to avoid the issues that can happen on screen orientation change while the AsyncTask is working.
I would go with method 2, but take it a step further.
Have a separate fragment to run your async task. This way, you can handle any configuration changes (not just rotating screen) without any issues.
In another fragment, you can display the data. You can pass the data from your async task fragment via callbacks to the activity, and have the activity call a method in the display fragment to update the data.

Is this the best way to implement AsyncTask? Or are there better ways?

I am trying to write a AsyncTask generic package. Till now, what I've done is call one activity from another send the instance in the Intent to that Activity. This activity is part of the AsyncTask which calls the AsyncLoader.execute() file. I am doing this so that I don't lose any data that the parent Activity sets on the layout.
The other way to do it would be to call the Intent and return the data from the AsyncActivity to the parent Activity. But, both of these methods are slower than implementing the AsyncTask in the parent activity.
But, that clutters up the code. Thus, is there a better way of implementing it?
It sounds like your tight-coupling between the activity and the AsyncTask is causing you issues that you're trying to overcome with a weird workaround.
In my experience the best way to design activities that need an AsyncTask is:
Keep your AsyncTask out of your activity, i.e. make a separate class
that extends AsyncTask. This allows you to reuse the AsyncTask
between multiple activities and make it easier to test.
If you need to return data back to your activity, use the listener and implement the listener on your activity. Then pass your listener to a class that creates the AsyncTask.
Passing of data between intents should be kept to a minimum, if you need to reuse the same AsyncTask from a separate activity you should follow the steps above and execute the task again. If you're going to be calling this through the lifecycle of the app, then consider using a service instead.

Categories

Resources