Deep dive
into Android async operations
Mateusz Grzechociński
Demo app source code
What’s it for?
Meet Adam
...and his story
Adam, a developer who knows
Requirements
Simple enough?
StrictMode
public class DemoApp extends Application {� @Override� public void onCreate() {� super.onCreate();� StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()� .detectDiskReads()� .detectDiskWrites()� .detectNetwork()� .penaltyLog()� .build());� }�}
"AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.”
Android SDK docs
DEMO 1
AsyncTask - multiple calls
< DONUT | DONUT | >=HONEYCOMB |
SERIAL | PARALLEL | SERIAL |
AsyncTask.executeOnExecutor(Executor)
DEMO 1a
AsyncTask - memory leak
Solutions
AsyncTask
Pros
Cons
Adam wonders: Really a good idea?
run task
→ change config (e.g. by rotating the screen)
→ cancel previous task
→ run new task
→ update UI
Loaders
Loaders, in particular CursorLoader, are expected to retain their data after being stopped. This allows applications to keep their data across the activity or fragment's onStop() and onStart() methods, so that when users return to an application, they don't have to wait for the data to reload.
Android SDK docs
AsyncTaskLoader
“This class performs the same function as the AsyncTask, but a bit better. It can handle Activity configuration changes more easily (add MG: by Loader Manager), and it behaves within the life cycles of Fragments and Activities.”
http://www.javacodegeeks.com/2013/01/android-loaders-versus-asynctask.html
How it works...
DEMO 2
Loader - first shoot and…
nothing
WHY?
@Override�protected void onStartLoading() {� super.onStartLoading();
Log.d(LOG_TAG, "onStartLoading");
+ forceLoad();�}
DEMO 3
Loader - forceLoad()
config change?
@Override�protected void onCreate(Bundle savedInstanceState) {� super.onCreate(savedInstanceState);� setContentView(R.layout.activity_matches);� if (savedInstanceState != null) {�+ getSupportLoaderManager().initLoader(LOADER_ID, args, this);
//restore state...� }�}
DEMO 4
Loader - config change
leaving activity?
@Override�protected void onStartLoading() {� super.onStartLoading();
Log.d(LOG_TAG, "onStartLoading");�+ if(result == null){� forceLoad();�+ }else{�+ deliverResult(result);�+ }�}
DEMO 5
Loader - leaving activity (home etc)
force refresh?
@Override�public void onLoadFinished(Loader<Match> loader, Match match) {� Log.d(LOG_TAG, "onLoadFinished: " + match);� currentMatches.add(match);� adapter.setCurrentMatches(currentMatches);� adapter.notifyDataSetChanged(); �+ getSupportLoaderManager().destroyLoader(LOADER_ID);�}
DEMO 6
Loader - force refresh
Loader
Pros
Cons
The idea
“[...]
Basically, what happens with RS is that when a request is being processed, its listeners will be invoked as long as the associated activity is alive.
[...]
The main purpose of RS is to make sure that there is no memory leak : your activity, if it has to die, will die and be garbage collected, RS doesn't hold any hard reference to it that would prevent garbage collection. That's really the core idea behind RoboSpice. [...]”
Stephen Nicolas
http://stackoverflow.com/questions/19011200/how-does-robospice-manage-activity-lifecycle
Show me do code!
DEMO 7
RoboSpice - basic
Config change
@Override�protected void onStart() {� super.onStart();� spiceManager.start(this);�+ spiceManager.addListenerIfPending(Match.class, CACHE_KEY, this);�}
Force refresh
private void loadMatchesOnClick() {� int matchID = 1;� MatchResultRequest request = new MatchResultRequest(Match.class, matchID);� spiceManager.execute(request, CACHE_KEY, DurationInMillis.ALWAYS_EXPIRED, this);�}
or
spiceManager.removeDataFromCache(Match.class, CACHE_KEY);
DEMO 8
RoboSpice - extended
By default
RoboSpice
Pros
Cons
Go Reactive
Motivations
"[...] If a calling Activity/Fragment makes multiple requests, it needs to handle responses in a single function by switching on the URL. This makes for code that isn’t very readable, especially when you have to jump from where the request is executed to where the response is handled"
Motivations
“It’s still a little verbose, but it’s more similar to writing synchronous code. I use the response in code that directly follows the request. I also get error handling on the UI thread for free.”
http://markhudnall.com/2013/10/15/rxjava-and-android/
Iterable | Observable |
getDataFromLocalMemory()� .skip(10)� .take(5)� .map({ s -> return s + " transformed" })� .forEach({ println "next => " + it }) | getDataFromNetwork()� .skip(10)� .take(5)� .map({ s -> return s + " transformed" })� .subscribe({ println "onNext => " + it }) |
getDataFromNetwork()� .skip(10)� .take(5)� .map({ s -> return s + " transformed" })� .subscribe({ println "onNext => " + it }) |
DEMO 9
RxAndroid in UI Layer
Worth trying?
@GET("/user/{id}/photo")�Observable<Photo> getUserPhoto(@Path("id") int id);
rxAndroid
Pros
Cons
Out of scope… or time
Remember
Thanks