Deep dive
into Android async operations
Mateusz Grzechociński
@mgrzechocinski
Demo app source code
https://github.com/mgrzechocinski/DroidconKrakow2014
What’s it for?
● Confitura 2010
● Review & share own experience
● ...and would appreciate your’s
● From developer to (intermediate) developers
Meet Adam
...and his story
Adam, a developer who knows
● what’s Looper.loop()
● what ANR is
● Long operation? Spawn a thread!
Requirements
● Bundesliga matches results
● Refresh on click
Simple enough?
● REST client
● 1 activity
● 1 button,
● refresh = call URLs in spawned threads
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
Performing network operations
DEMO 1
AsyncTask - multiple calls
< DONUT DONUT >=HONEYCOMB
SERIAL PARALLEL SERIAL
AsyncTask.executeOnExecutor(Executor)
DEMO 1a
AsyncTask - memory leak
Solutions
● Track activity/fragment lifecycle manually
○ cancel it?
● Use fragment with setRetainInstance
(true)
AsyncTask
Pros
● Simple
● Great for few seconds
operations
● Cancellation support
Cons
● Memory leaks,
● Possible crashes
● Behaves different
● Lack of exceptions handling
(or RoboAsyncTask)
http://stackoverflow.com/questions/3357477/is-asynctask-really-conceptually-flawed-or-am-i-just-
missing-something
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
● HONEYCOMB+
● support-v4
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...
● LoaderManager
● initLoader
● LoaderCallback
● CursorLoader
● AsyncTaskLoader
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
● Better than AsyncTask, much
● Gives “partial” caching
○ Need in-loader result
caching when leaving
activity
○ No control
● No memory leaks
Cons
● Designed to support DB data
(cursor)
● Hacking, hacking, hacking to
use in networking
● Poorly documented
● Opposite to AsyncTask
○ too highly coupled
● Poor exception management
https://github.com/stephanenicolas/robospice/wiki/A-User's-Perspective-on-RoboSpice
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
https://www.youtube.com/watch?v=xHXn3Kg2IQE
Show me do code!
DEMO 7
RoboSpice - basic
● config change
● refresh() → no caching
@Override
protected void onStart() {
super.onStart();
spiceManager.start(this);
+ spiceManager.addListenerIfPending(Match.class, CACHE_KEY, this);
}
Config change
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
● Handling config changes
● Full control caching
● Multithreading
● No internet connection → NoNetworkException.class
● Modules to SpringAndroid, Retrofit
● and much more...
RoboSpice
Pros
● Robust, full featured
● Last commit < 3 months ago
Cons
● POJO per Request
● No a simple library
○ SpiceManager with 1300
LOC
Go Reactive
● ReactiveExtentions by MS
● Ported by Netflix
● rxAndroid = rxJava + few Android classes
● rxJava hit 1.0
● rxAndroid still 0.x
https://github.com/ReactiveX/RxJava/wiki
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"
http://markhudnall.com/2013/10/15/rxjava-and-android/
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?
● Streams of events
○ e.g. Weather station, tweets analyzer
● In “common” app?
● When using Retrofit
@GET("/user/{id}/photo")
Observable<Photo> getUserPhoto(@Path("id") int id);
rxAndroid
Pros
● Reduced callback hell with
FPP
● Robust set of
functions/transformations
Cons
● Need to change the way of
thinking
● Hard, really
● On Android - even harder
● Possible memory leaks?
● Debugging?
Out of scope… or time
● IntentService (!= Service)
● Event buses (Otto, GreenRobot’s EventBus)
○ especially with Loaders
● Volley
● Ion
Remember
● Don’t use AsyncTask
● Consider using Loaders
● Give a try RoboSpice
● Learn FRP (rxJava/rxAndroid)
Thanks
Mateusz Grzechociński
@mgrzechocinski
http://grzechocinski.net

Deep dive into Android async operations

  • 1.
    Deep dive into Androidasync operations Mateusz Grzechociński @mgrzechocinski Demo app source code https://github.com/mgrzechocinski/DroidconKrakow2014
  • 2.
    What’s it for? ●Confitura 2010 ● Review & share own experience ● ...and would appreciate your’s ● From developer to (intermediate) developers
  • 3.
  • 4.
  • 5.
    Adam, a developerwho knows ● what’s Looper.loop() ● what ANR is ● Long operation? Spawn a thread!
  • 6.
    Requirements ● Bundesliga matchesresults ● Refresh on click
  • 7.
    Simple enough? ● RESTclient ● 1 activity ● 1 button, ● refresh = call URLs in spawned threads
  • 8.
    StrictMode public class DemoAppextends Application { @Override public void onCreate() { super.onCreate(); StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectDiskReads() .detectDiskWrites() .detectNetwork() .penaltyLog() .build()); } }
  • 9.
    "AsyncTask enables properand 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
  • 10.
  • 11.
    DEMO 1 AsyncTask -multiple calls
  • 12.
    < DONUT DONUT>=HONEYCOMB SERIAL PARALLEL SERIAL AsyncTask.executeOnExecutor(Executor)
  • 13.
  • 15.
    Solutions ● Track activity/fragmentlifecycle manually ○ cancel it? ● Use fragment with setRetainInstance (true)
  • 16.
    AsyncTask Pros ● Simple ● Greatfor few seconds operations ● Cancellation support Cons ● Memory leaks, ● Possible crashes ● Behaves different ● Lack of exceptions handling (or RoboAsyncTask) http://stackoverflow.com/questions/3357477/is-asynctask-really-conceptually-flawed-or-am-i-just- missing-something
  • 17.
    Adam wonders: Reallya good idea? run task → change config (e.g. by rotating the screen) → cancel previous task → run new task → update UI
  • 18.
  • 19.
    Loaders, in particularCursorLoader, 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
  • 20.
    AsyncTaskLoader “This class performsthe 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
  • 21.
    How it works... ●LoaderManager ● initLoader ● LoaderCallback ● CursorLoader ● AsyncTaskLoader
  • 22.
    DEMO 2 Loader -first shoot and… nothing WHY?
  • 24.
    @Override protected void onStartLoading(){ super.onStartLoading(); Log.d(LOG_TAG, "onStartLoading"); + forceLoad(); }
  • 25.
    DEMO 3 Loader -forceLoad() config change?
  • 26.
    @Override protected void onCreate(BundlesavedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_matches); if (savedInstanceState != null) { + getSupportLoaderManager().initLoader(LOADER_ID, args, this); //restore state... } }
  • 27.
    DEMO 4 Loader -config change leaving activity?
  • 28.
    @Override protected void onStartLoading(){ super.onStartLoading(); Log.d(LOG_TAG, "onStartLoading"); + if(result == null){ forceLoad(); + }else{ + deliverResult(result); + } }
  • 29.
    DEMO 5 Loader -leaving activity (home etc) force refresh?
  • 30.
    @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); }
  • 31.
    DEMO 6 Loader -force refresh
  • 32.
    Loader Pros ● Better thanAsyncTask, much ● Gives “partial” caching ○ Need in-loader result caching when leaving activity ○ No control ● No memory leaks Cons ● Designed to support DB data (cursor) ● Hacking, hacking, hacking to use in networking ● Poorly documented ● Opposite to AsyncTask ○ too highly coupled ● Poor exception management
  • 33.
  • 34.
    The idea “[...] Basically, whathappens 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
  • 35.
  • 36.
  • 37.
    DEMO 7 RoboSpice -basic ● config change ● refresh() → no caching
  • 38.
    @Override protected void onStart(){ super.onStart(); spiceManager.start(this); + spiceManager.addListenerIfPending(Match.class, CACHE_KEY, this); } Config change
  • 39.
    Force refresh private voidloadMatchesOnClick() { 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);
  • 40.
  • 41.
    By default ● Handlingconfig changes ● Full control caching ● Multithreading ● No internet connection → NoNetworkException.class ● Modules to SpringAndroid, Retrofit ● and much more...
  • 42.
    RoboSpice Pros ● Robust, fullfeatured ● Last commit < 3 months ago Cons ● POJO per Request ● No a simple library ○ SpiceManager with 1300 LOC
  • 43.
    Go Reactive ● ReactiveExtentionsby MS ● Ported by Netflix ● rxAndroid = rxJava + few Android classes ● rxJava hit 1.0 ● rxAndroid still 0.x
  • 44.
  • 45.
    Motivations "[...] If acalling 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" http://markhudnall.com/2013/10/15/rxjava-and-android/
  • 46.
    Motivations “It’s still alittle 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/
  • 47.
    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 })
  • 48.
    getDataFromNetwork() .skip(10) .take(5) .map({ s ->return s + " transformed" }) .subscribe({ println "onNext => " + it })
  • 49.
  • 50.
    Worth trying? ● Streamsof events ○ e.g. Weather station, tweets analyzer ● In “common” app? ● When using Retrofit @GET("/user/{id}/photo") Observable<Photo> getUserPhoto(@Path("id") int id);
  • 51.
    rxAndroid Pros ● Reduced callbackhell with FPP ● Robust set of functions/transformations Cons ● Need to change the way of thinking ● Hard, really ● On Android - even harder ● Possible memory leaks? ● Debugging?
  • 52.
    Out of scope…or time ● IntentService (!= Service) ● Event buses (Otto, GreenRobot’s EventBus) ○ especially with Loaders ● Volley ● Ion
  • 53.
    Remember ● Don’t useAsyncTask ● Consider using Loaders ● Give a try RoboSpice ● Learn FRP (rxJava/rxAndroid)
  • 54.