Top Banner
Deep dive into Android async operations Mateusz Grzechociński @mgrzechocinski Demo app source code https://github.com/mgrzechocinski/DroidconKrakow2014
54

Deep dive into Android async operations

Jul 15, 2015

Download

Technology

Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Deep dive into Android async operations

Deep diveinto Android async operations

Mateusz Grzechociński@mgrzechocinski

Demo app source codehttps://github.com/mgrzechocinski/DroidconKrakow2014

Page 2: Deep dive into Android async operations

What’s it for?

● Confitura 2010

● Review & share own experience

● ...and would appreciate your’s

● From developer to (intermediate) developers

Page 3: Deep dive into Android async operations

Meet Adam

Page 4: Deep dive into Android async operations

...and his story

Page 5: Deep dive into Android async operations

Adam, a developer who knows

● what’s Looper.loop()

● what ANR is

● Long operation? Spawn a thread!

Page 6: Deep dive into Android async operations

Requirements

● Bundesliga matches results

● Refresh on click

Page 7: Deep dive into Android async operations

Simple enough?

● REST client● 1 activity● 1 button, ● refresh = call URLs in spawned threads

Page 8: Deep dive into Android async operations

StrictModepublic class DemoApp extends Application {

@Override

public void onCreate() {

super.onCreate();

StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()

.detectDiskReads()

.detectDiskWrites()

.detectNetwork()

.penaltyLog()

.build());

}

}

Page 9: Deep dive into Android async operations

"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

Page 11: Deep dive into Android async operations

DEMO 1AsyncTask - multiple calls

Page 12: Deep dive into Android async operations

< DONUT DONUT >=HONEYCOMB

SERIAL PARALLEL SERIAL

AsyncTask.executeOnExecutor(Executor)

Page 13: Deep dive into Android async operations

DEMO 1aAsyncTask - memory leak

Page 14: Deep dive into Android async operations
Page 15: Deep dive into Android async operations

Solutions

● Track activity/fragment lifecycle manually○ cancel it?

● Use fragment with setRetainInstance

(true)

Page 16: Deep dive into Android async operations

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

Page 17: Deep dive into Android async operations

Adam wonders: Really a good idea?

run task→ change config (e.g. by rotating the screen)

→ cancel previous task→ run new task

→ update UI

Page 18: Deep dive into Android async operations

Loaders

● HONEYCOMB+● support-v4

Page 19: Deep dive into Android async operations

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

Page 20: Deep dive into Android async operations

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

Page 21: Deep dive into Android async operations

How it works...

● LoaderManager● initLoader● LoaderCallback● CursorLoader● AsyncTaskLoader

Page 22: Deep dive into Android async operations

DEMO 2Loader - first shoot and…

nothing

WHY?

Page 23: Deep dive into Android async operations
Page 24: Deep dive into Android async operations

@Override

protected void onStartLoading() {

super.onStartLoading();

Log.d(LOG_TAG, "onStartLoading");

+ forceLoad();

}

Page 25: Deep dive into Android async operations

DEMO 3Loader - forceLoad()

config change?

Page 26: Deep dive into Android async operations

@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...

}

}

Page 27: Deep dive into Android async operations

DEMO 4Loader - config change

leaving activity?

Page 28: Deep dive into Android async operations

@Override

protected void onStartLoading() {

super.onStartLoading();

Log.d(LOG_TAG, "onStartLoading");

+ if(result == null){

forceLoad();

+ }else{

+ deliverResult(result);

+ }

}

Page 29: Deep dive into Android async operations

DEMO 5Loader - leaving activity (home etc)

force refresh?

Page 30: Deep dive into Android async operations

@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);

}

Page 31: Deep dive into Android async operations

DEMO 6Loader - force refresh

Page 32: Deep dive into Android async operations

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

Page 34: Deep dive into Android async operations

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 Nicolashttp://stackoverflow.com/questions/19011200/how-does-robospice-manage-activity-lifecycle

Page 36: Deep dive into Android async operations

Show me do code!

Page 37: Deep dive into Android async operations

DEMO 7RoboSpice - basic

● config change● refresh() → no caching

Page 38: Deep dive into Android async operations

@Override

protected void onStart() {

super.onStart();

spiceManager.start(this);

+ spiceManager.addListenerIfPending(Match.class, CACHE_KEY, this);

}

Config change

Page 39: Deep dive into Android async operations

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);

Page 40: Deep dive into Android async operations

DEMO 8RoboSpice - extended

Page 41: Deep dive into Android async operations

By default● Handling config changes

● Full control caching

● Multithreading

● No internet connection → NoNetworkException.class

● Modules to SpringAndroid, Retrofit

● and much more...

Page 42: Deep dive into Android async operations

RoboSpice

Pros● Robust, full featured● Last commit < 3 months ago

Cons● POJO per Request● No a simple library

○ SpiceManager with 1300 LOC

Page 43: Deep dive into Android async operations

Go Reactive

● ReactiveExtentions by MS

● Ported by Netflix

● rxAndroid = rxJava + few Android classes

● rxJava hit 1.0

● rxAndroid still 0.x

Page 45: Deep dive into Android async operations

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/

Page 46: Deep dive into Android async operations

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/

Page 47: Deep dive into Android async operations

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 })

Page 48: Deep dive into Android async operations

getDataFromNetwork()

.skip(10)

.take(5)

.map({ s -> return s + " transformed" })

.subscribe({ println "onNext => " + it })

Page 49: Deep dive into Android async operations

DEMO 9RxAndroid in UI Layer

Page 50: Deep dive into Android async operations

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);

Page 51: Deep dive into Android async operations

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?

Page 52: Deep dive into Android async operations

Out of scope… or time

● IntentService (!= Service)

● Event buses (Otto, GreenRobot’s EventBus)○ especially with Loaders

● Volley

● Ion

Page 53: Deep dive into Android async operations

Remember

● Don’t use AsyncTask

● Consider using Loaders

● Give a try RoboSpice

● Learn FRP (rxJava/rxAndroid)