Suppose I have a situation where an API response fills my Musician model with information specific to that musician. There is another API response that, given a musician name, describes related artists.
I want to launch a detail activity showing the musician's name, members, genre, albums(API response A) along with related artists(API response B).
An asynchronous network call will provide a better user experience, but since launching the detail activity is dependent on 2 different api calls, is there a better solution than having API call be asynch and API call B be synch(blocking A and the UI)?
If I make both asynchronous, API call B could be running while the activity changes, stopping it(since an asynctask is destroyed when the activity changes). This would result in missing data in the detail activity.
In a simple case:
Start the detail activity
Execute both requests asynchronously while presenting the user with a loading UX
Once is retrieved, update the UI accordingly
You can change the layout of an Activity at any time at runtime without recreating it.
You can either update the UI immediately with "Response A" and then again with "Response B" or wait for both responses to return and then update the UI; the former is preferable.
Network operations should never block the UI or the user. Displaying loading spinners and such is perfectly acceptable and is used all the time in Google's own apps.
Related
If I need to display data coming from an API in a Fragment (using an AsyncTask), let's say a list of items in a RecyclerView, I believe it shouldn't be done in onCreate() or onCreateView() since theoretically the view elements are being initialized and may not be ready to use if the call to the API is faster. Am I correct?
(I assume it's kind of impossible to get a response from an API in less time than it takes for Android to create the view though).
There is onActivityCreated() and onStart() but I am still confused about when the parent Activity calls them.
The thing I want to avoid is reloading data (making a call to the API) if it's not necessary, for instance because of an orientation change or going back to this Activity after a click on the back button from a possible "next" Activity.
Thanks.
If it is your first "window" (Activity or whatever) the only way I know is showing a loading text, image, etc.
If it isn't your first view you can load your data in another window and store it to later usage when the user reaches the View to show the info.
You can load it right in your onCreate, onStart or so, but as you said probably you won't have time to download the info, so again, show a loading page or whatever you want while your data comes.
To avoid initialization errors call your AsyncTask after initializing the elements. And to avoid calling the API multiple times save your data locally while the app is opened, it depends on your app requirements
I am trying to understand some finer points of AsyncTaskLoaders. This may be obvious, to others but I can't find an unambiguous example or definition that demonstrates and exmplains what happens when you override the deliverResult() method. What actually gets delivered ? How does this interact with the calling object ? I can see use of super.deliverResult, which passes a private object from the class. So, does the loader automatically know what to associate with the "delivered result". I am totally confused.
Seems I'm a bit late to the party, but anyway...
One of the main advantages of this intermediary step between the background loading and the UI thread's callback onLoadFinished() getting called
loadInBackground()
deliverResult() and
the callback onLoadFinished()
is that it gives us a means of shortcutting the whole loading process from within the AsyncTaskLoader class.
And this can be put to good use for caching the loading result within your AsyncTaskLoader and preventing the background loading from happening if there is cached data.
And why would we want to do this? Isn't the whole point of loaders dealing with those dreaded activity lifecycle issues (e.g. rotating the device), maintaining state (like, caching data) and having a means to get updated when underlying data changes (CursorLoader)?
Well, yes, but this isn't the whole story.
Consider this use case:
You've got your app (the one with the AsynTaskLoader) up-and-running and it already has loaded data into your UI.
Then, you switch over to your Twitter app to check on some news and return to you app.
Without caching, upon returning to your app, the loader would do its reloading.
This behavior is different from the one after configuration changes, e.g. rotating your device, in which case no reloading would take place.
So, how would we then prevent the loader from re-fetching data in case we're just sending our app to the background and, later, return to it again?
Solution
Create a cache member variable in your AsyncTaskLoader implementation.
Override deliverResult() so that you save your fetched data in your cache first, before you call the superclass's implementation of deliverResult().
In onStartLoading() check if there's cached data, and if so, let your AsyncTaskLoader just deliver that. Otherwise, start loading.
Here's a link to a sample app which implements this behaviour.
It's just a "Toy app" and as such part of Udacity's current version of the "Developing Android Apps" fundamentals course. And here is the link to the respective video within that course that deals with this issue. (The course is free, but you'll still have to sign-up w/ Udacity).
In short, what this app demonstrates, is a UI in which the user can input a search query for searching GitHub's repos (via the GitHub API), showing the resulting search URL in a TextView and also the raw JSON fetched from GitHub in another TextView.
The whole action happens in just MainActivity.java and the relevant part here is within the AsyncTaskLoader that's implemented as an anonymous inner class:
For step 1, just introduce a member variable in your AsyncTaskLoader implementation that's meant to serve as your data cache.
/* This String will contain the raw JSON
from the results of our Github search */
String mGithubJson;
For step 2, override deliverResult() as to cache the loading result.
When loadInBackground() has finished, it passes its return value to deliverResult().
It does so anyway, but now that we've overridden deliverResult() we can step right in and store our fetched data into the cache member variable which we've created with just so good foresight.
And finally, we chain up to the super class implementation of deliverResult() with super.deliverResult() which will pass-on the result to the callback method onLoadFinished(), running on the UI thread.
#Override
public void deliverResult(String githubJson) {
mGithubJson = githubJson;
super.deliverResult(githubJson);
}
For step 3, check in onStartLoading() whether or not we've got cached data.
If we don't have cached data (yet), just force the loading to begin with a call to forceLoad().
But if we do have cached data, just call deliverResult(yourCachedDataGoesHere) and pass-in the cached data as argument.
if (mGithubJson != null) {
deliverResult(mGithubJson);
} else {
forceLoad();
}
So, if you now switch back and forth between your app and some other app(s), you'll notice that no reloading takes place, as the loader will just use your cached data.
suppose when data are loading in the background, at this time, user press HOME button and exist the app, when user comes back to the app, loading has been finished. So we have already have the data, then AsyncTaskLoader will call the deliverResult() method, deliver the data to the onLoadFinished() method for displaying.
When the user come back to app, onStartLoading() is being called before loadInBackground(). In this method, we could check if our data if empty or not, if not empty, we call deliverResult() and send the result to onLoaderFinished(), so it could prevent to reload data.
When we press HOME exist the app and then come back, it will not create a new Loader, instead the old loader will try to load data.
The only answer I can find that makes any sense is based on a decription in this link.
"A registered listener to receive the Loader's results when it
completes a load. For each of its Loaders, the LoaderManager
registers an OnLoadCompleteListener which will forward the Loader’s
delivered results to the client with a call to
onLoadFinished(Loader loader, D result). Loaders should deliver
results to these registered listeners with a call to
Loader#deliverResult(D result)."
deliverResult appears to be used when you have listeners to the AsyncTask and want to send the results back to them. I would say it's uncommon. The Android documentation is even less descriptive:
"Sends the result of the load to the registered listener. Should only
be called by subclasses. Must be called from the process's main
thread.
Parameters
data : the result of the load"
deliverResult works after doInbackground completes. It sends the result D (returned by doInBackground) to the calling thread. You may wish to override it for cleaning data, but you can do clean-up in doInBackground instead without overriding deliverResult.
Is there any way I can go from one activity to another and delay the display of the screen on the target activity?
What I want to be able to do is to allow the target activity to fetch its required data but not to display anything until does.
I want the screen of the source activity to still be visible until I am ready with the data in the second activity.
Specifically, I am using an AsyncTask to fetch the data.
I know I could fetch the data in the source activity and then send it on to the target activity but this is not viable in our case.
Edit: More Info:
The whole reason I want this is because I am trying to change the structure of certain parts of the current code.
At present the way it works is the the first activity gets the data and then sends it to the second activity as a bundle.
This created problems when the second activity could be invoked from multiple places. It resulted in loads of duplicate code.
So, I decided to move the fetching of the data into the target activity thus cutting out any need for repeating code.
it also makes more sense for the activity to fetch its own data rather than relying on something else sending it.
You should first make a service that runs your async task. Then, start the service from your first activity with startService(new Intent(this, UpdaterServiceManager.class));
When the task ends in the service, start the second activity.
Click here for an excellent service tutorial.
Try to use service for this purpose.
I would like to write an app to follow the price of lists of stocks.
There would be three activities containing lists of stocks :
myStocksActivity containing the stocks I'm interested in
searchedStocksActivity containing a list of stocks I could add in the list contained in myStocksActivity
winnersLoosersStocksActivity containing the list of stocks with the best or worst performance of the day.
I would like to persist the list of stocks in myStocksActivity, to be able to remove or add stocks from that list and to add the stocks displayed in searchedStocksActivity or winnersLoosersStocksActivity to myStocksActivity.
Another challenge : the price of the stocks should be updated every X minutes depending on the settings of app. The prices should only be updated when the app is running and should only update the prices of the list I'm currently looking at.
For the moment, my architecture isn't great : almost all the logic is contained in myStocksActivity and I know it's not a good thing.
I was thinking about moving the update of the stock list to a service, but I don't want this service to run when the application is not running.
What do you think ? Is my problem clear ? Thank you !
If I was you I would try and design(and build) a domain model first. This should end up being a set of classes which allows you to do everything you want with your stocks, independently of the a UI. You should also build in data persistence directly into these classes (i suggest using SQLite for this bit).
Then once you have a working model, build the UI on top of that. The MVP design pattern works pretty well with android.
Implement your activities as Views, these should both present data, and captures UI events and delegate these events to to Presenter instances, which then communicate/manipulate the model, and updates the Views accordingly. For example,
MyStocksView could present the user with a list of stocks, and the latest movements of stock price (or whatever). The MyStocksView contains the actual widgets that make up the user interface, and also acts as event listener and handler for various UI events (like when the user click a button). It should also contain a instance of a MyStocksPresenter class.
Once the user clicks the button, lets say, "remove stock", the event handler of MyStocksView then fires a method in the presenter instance for example, presenter.removeStock(id) , this method then updates the model (removes it from the in-memory data structures and database) and finally, if everything was successful, updates the View. It basically works as a middle man between the presentation layer, and the data-model.
In regards to the automatic updates every X minutes, I would handle this with a AsyncTask, there's not really much point in using a service if you only want this to happen while you app is running.
Whenever you main activity gets paused (or destroyed), call StopService(blabla);.
That way you can keep it as a service, and it won't run in the background.
Create a class for a stock, and store the update logic in there
I would put the handler - what holds instances of the stock-class and loops over a set to tell them to update - either in its own class purely with static methods and variables, or also in the stock class with static methods/etc.
The service then contains timing information, and calls the static update() method
The activity that displays stocks starts/stops the service with onCreate/onDestroy or onUpdate/onPause (I think it's update; the on that happens each time the Activity is brought to the forefront)
Same activity tells the handler class which ones to load, and uses its list for the display.
One other thing: I'd suggest against using a service. Use an AsyncTask. Services actually do run on the UI thread, so if there's a long-running loop during the update, the UI will freeze. AsyncTask starts a thread and will not block the UI.
I read a lot about handling rotation in android applications, but I still have so many questions and need to much to understand.
Let me explain my problem or implementation, that I'm using now in my application.
If an activity will be opened, a get request will be sent to server. This request will be executed in a Thread (new Thread(...)) and if request was completed, activity's ui will be refreshed.
But what should I do, if the user rotate his device?
By default, the activity will be destroyed and request will be started again and start a new thread, but the thread of destroyed activity may be still running.
I guess, it's a quite wrong way, I have now.
But what is the best approach, to handle this?
Probably is the best way to forbid rotation, but what If I don't want that?!
May be it's the second part of my question:
I saw a video form Google IO. Mr. Dobjanschi suggested to use services and to store retrieved data in content provider. So, probably I can use a service for executing my requests. But should data be replaced every time the get request was completed?!
Well dont know exactly how its done, You can try saving the instance and retrieving the same when config changes with following methods:
I have read about them but haven't really implemented them yet. I hope it can give you some start.
#Override
public Object onRetainNonConfigurationInstance() {
return(myServerThread);
}
private void restoreServerFunctions() {
if (getLastNonConfigurationInstance()!=null) {
myServerThread=(Thread)getLastNonConfigurationInstance();
}
}
You can specify that the activity handles the rotation itself. This is done through adding:
android:configChanges="keyboardHidden|orientation"
in the tag of the activity inside your android manifest. You don't have to actually handle the rotation but this will tell android to not destroy your activity. The base activity class will handle all the rotating of the user interface for you and your thread will be executed correct.
A small side note: if you are doing only a small server task use AsyncTask to execute the call to the server in the background instead of creating a thread. This will minimze some of the programming effort you need to communicate the results from the thread to the activity and update your UI.
One easy way, though I've never tried it. Instead of refreshing the current UI, when the thread finishes, start a new Activity with the just downloaded content. So first, you start an Activity with a blank page (or just the page's frame), then you rotate the blank page as much as you like, then the downloading Thread spawns a new Activity, replacing the blank page Activity with the loaded content page using the current orientation.