programming sideways: asynchronous techniques for android
DESCRIPTION
Android apps need to respond fast, support highly parallel execution and multi component architecture. Learn some tricks of the trade for these problems! as presented at www.mobileconference.it (2013 edition)TRANSCRIPT
ASYNCHRONOUS TECHNIQUES FOR ANDROID: PROGRAMMING SIDEWAYS
Emanuele Di Saverio
AND02
Senior Design Technologist
[email protected] hazam emanueledisaverio
Who?
"Agilist in a Mobile world" • Mobile Java Developer (2007) • Android (2009) • h@p://www.androidavanzato.it (2012)
Emanuele Di Saverio Senior Design Technologist @
TWO RECURRING PROBLEMS, WITH SIMILAR SYMPTOMS
1ST PROBLEM
A (biased) TALE ABOUT SERVERS
The S in C/S
• a server program in (connected) network compuKng
while (true) {connect();read();parseRequest();process();sendResponse();disconnect();
}
MPM Prefork (Apache)
• let's fork() and spawn more processes!
full address space copied around
MPM Worker (Apache)
• handful processes spawn a set of threads
context switch + shared memory
Multi Threaded Programming
• Memory is shared, threads spawn faster • Problem of synchronizaKon – solvable only with low-‐level OS primiKves
Multi Threaded Programming
• Memory is shared, threads spawn faster • Problem of synchronizaKon – solvable only with low-‐level OS primiKves
"… non-‐trivial mul/-‐threaded programs are incomprehensible to humans. It is true that the programming model can be improved: design pa;erns, atomicity, improved languages, and formal methods. However, these techniques merely chip away at the unnecessarily enormous non-‐determinism of the threading model. The model remains intrinsically intractable."
Edward A. Lee – The Problem with Threads
CONTEXT SWITCH has fixed cost.
Context Switch
Context Switch
hint: human mind work same way
Single Thread Event
Old technique – New Trend
Nginx / NodeJS / Jetty
• User Code is executed by single thread
• I/O is on separate thread
MOBILE UI ARCHITECTURE a.k.a. looked from a high enough level, they're all the same.
The Loop
while (true) {processInputEvents();processSysEvents();measure();layout();draw();
}
input
sys
measure
layout
draw
thread
Ok, when it is my turn?
HOLLYWOOD PRINCIPLE
"don't call me, I'll call you" managed execuKon
LISTENERS
Button a;[...]a.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {//do stuff}
});
object that receives callback through define interface
class MyActivity extends Activity {protected void onCreate(Bundle b) {
//create my UI}onStop() {...}onStart(){...}}
The Android Loop
All callbacks are executed in Main Thread! • AcKvity • Service • Fragment • animaKons • OnClicks • OnTouch • OnWhatever...
android.os.Looper
Make every thread an event-‐based message queue
public void run() {Looper.prepare();this.handler = new Handler() handleMessage(int what) if (what == 0x666) //do stuff};Looper.loop();}
handler.sendMessage(Message.obtain(0x666));
60 FPS -> 16 ms per frame ALL YOUR CODE NEEDS TO FIT IN 16ms
The Loop
while (true) {processInputEvents();processSysEvents();measure();layout();draw();
}
input
sys
measure
layout
draw
thread 16ms
DON'T BLOCK UI THREAD no network calls -‐ no file I/O -‐ no intensive computaKon
DON'T BLOCK UI THREAD no network calls -‐ no file I/O -‐ no intensive computaKon
USE OTHER THREADS!
Asynchronous Java
Java was designed with multithreaded programming in mind (thread ,synchronized, volatile, memory model). Don't rely on that – probabilities you really understand what they do are small. java.util.concurrent.* full of goodies to handle concurrency
Asynchronous Java
You don't really create Threads that much Executors – Thread pool implemented for you
ExecutorService executor = Executors.newFixedThreadPool(10);executor.execute(new Runnable() {
run() { //do stuff}
});
Future – a computation Stock Option
Want to have result of an async execuKon?
Future<String> f1 = executor.submit(new Callable<String> {String call() { return fetchNetworkContent();}
});
f1.get(); //locks current thread
Future – combining? Future<String> f1 = executor.submit(...);Future<Integer> f2 = executor.submit(...); Future<Integer> f3 = executor.submit(new [CALLABLE](f1.get()));Future<Integer> f4 = executor.submit(new [CALLABLE]f2.get()));
Future – combining? Future<String> f1 = executor.submit(...);Future<Integer> f2 = executor.submit(...); Future<Integer> f3 = executor.submit(new [CALLABLE](f1.get()));Future<Integer> f4 = executor.submit(new [CALLABLE]f2.get()));Future<String> f3 = executor.submit(new Callable<String>() {call() {return new CallToRemoteServiceC(f1.get()).call();
}});
Future – Other Approaches List<Future> futures = new ArrayList<Future>();Iterator<Future<?>> i = futures.iterator();while (i.hasNext()) { Future<?> f = i.next(); if (f.isDone()) { doMoreWork(f.get());
i.remove();} }
GET() BLOCKS COMBINATION IS A PAIN
Callback
Gets around the early blocking problem by pre-‐declaring the funcKon that will be executed
public interface Callback<T> { public void done(T result, Exception error);
}
Callback fetchNetworkContentAsync(Callback<Response> cb) {mExecutor.execute(new Runnable(){...try { Response r = fetchNetworkContent(); cb.done(r, null);} catch(Exception exc) { cb.done(null, exc);});
}
WAIT, WE NEED TO BE IN MAIN THREAD
Callback fetchNetworkContentAsync(Callback<Response> cb) {mExecutor.execute(new Runnable(){...try { Response r = fetchNetworkContent(); mHandler.post(new Runnable() { cb.done(r, null); });} catch(Exception exc) { cb.done(null, exc);});
}
Callback fetchNetworkContentAsync(Callback<Response> cb) {mExecutor.execute(new Runnable(){...try { Response r = fetchNetworkContent(); mExecutor.execute(new Runnable() { try { Response r2 = fetchNetworkContent2(r); mHandler.post(new Runnable() { cb.done(r2, null); });
} catch(Exception exc) { cb.done(null, exc);});
});} catch(Exception exc) { cb.done(null, exc);});
}
Callback fetchNetworkContentAsync(Callback<Response> cb) {CountDownLatch cd = new CountDownLatch(2);Response r1, r2;mExecutor.execute(new Runnable(){ Response r1 = fetchNetworkContent(); cd.countDown();});mExecutor.execute(new Runnable(){ Response r2 = fetchNetworkContent(); cd.countDown();});mExecutor.execute(new Runnable(){ cd.await(); Response r = new Response(r1,r2); mHandler.post(new Runnable() { cb.done(r, null); });});
}
ANDROID SDK HELP
Loader & AsyncTask
They help in the field of gedng the asynchronous task happen in a way that is compaKble with Android threading model, but fall short in: • Combining • ManipulaKng • Error Handling • Streaming Data Handling
AsyncTask
NEED MORE ASYNC EXPRESSIVENESS a.k.a. my head is hurKng
ENTER REACTIVE EXTENSIONS
Reactive Extensions (Rx)
Developed by Microsof for .NET (C#) 2009 – Erik Meijer -‐ to support distributed cloud applicaEons Open Source port of the semanKcs to • Rx.ObjC, RxJava, Rx.JS, Scala
• JavaVM based version is developed by Nejlix – Ben Christensen
Rx (Java): Concepts
Observable EnKty that can fetch a stream of events asyncronously, if you subscribe an observer to it Observer You can imperaKvely noKfy an observer about an event
Rx (Java): Sample Observable<Employee> loadEmployeeWithName(String name) { Observable<Employee> observable = Observable.create(
new Func1<Observer<Employee>, Subscription>() { public Subscription call(final Observer<Employee> observer) {
List<Employee> employee = loadEmployeeFromDB(name); for (Employee emp : employees) { try {
observer.onNext(emp); } catch (Exception e) { observer.onException(e); }}
observer.onCompleted(); return Subscriptions.empty(); } });return observable;}
loadEmployeeWithName("Aldo").subscribe(new Observer<Employee>() {
public void onNext(Employee emp) { showEmployeeDataOnScreen(emp); } public void onError(Exception e) { handleException(e); }
public void onCompleted() { doneLoadingEmployees();
}});
"Async" expressed for collections
IMPERATIVE
• T next() • throws ExcepKon • return
REACTIVE
• onNext(T) • onError(ExcepKon) • onCompleted()
Rx (Java): Sample Observable<Employee> loadEmployeeWithName(String name) { Observable<Employee> observable = Observable.create(
new Func1<Observer<Employee>, Subscription>() { public Subscription call(final Observer<Employee> observer) {
List<Employee> employee = loadEmployeeFromDB(name); for (Employee emp : employees) { try {
observer.onNext(emp); } catch (Exception e) { observer.onException(e); }}
observer.onCompleted(); return Subscriptions.empty(); } });return observable;}
loadEmployeeWithName("Aldo").subscribe(new Observer<Employee>() {
public void onNext(Employee emp) { showEmployeeDataOnScreen(emp); } public void onError(Exception e) { handleException(e); }
public void onCompleted() { doneLoadingEmployees();
}});
STILL SYNC
Rx (Java): Sample Observable<Employee> loadEmployeeWithName(String name) { Observable<Employee> observable = Observable.create(
new Func1<Observer<Employee>, Subscription>() { public Subscription call(final Observer<Employee> observer) { executor.postRunnable(new Runnable() {...
List<Employee> employee = loadEmployeeFromDB(name);
for (Employee emp : employees) { try { mHandler.post(new Runnable() { ...
observer.onNext(emp); ... } } catch (Exception e) {
mHandler.post(new Runnable() { ...
observer.onException(e); }) }}
mHandler.post(new Runnable() { observer.onCompleted(); });
return Subscriptions.empty(); } }); });return observable;}
So what is Rx really doing?
Rx is man-‐in-‐the middle between method calling and callback execuKon
onNext() != onNext()
We have control over scheduling of methods
Rx (Java): Sample Observable<Employee> loadEmployeeWithName(String name) { Observable<Employee> observable = Observable.create(
new Func1<Observer<Employee>, Subscription>() { public Subscription call(final Observer<Employee> observer)
{ List<Employee> employee = loadEmployeeFromDB(name);
for (Employee emp : employees) { try {
observer.onNext(emp); } catch (Exception e) { observer.onException(e); }}
observer.onCompleted(); return Subscriptions.empty(); } });observable = observable.subscribeOn(Schedulers.executor(mExecutor));observable = observable.observeOn(AndroidScheduler.getInstance());return observable;}
Rx (Java): Sample Observable<Employee> loadEmployeeWithName(String name) { Observable<Employee> observable = Observable.create(
new Func1<Observer<Employee>, Subscription>() { public Subscription call(final Observer<Employee> observer)
{ List<Employee> employee = loadEmployeeFromDB(name);
for (Employee emp : employees) { try {
observer.onNext(emp); } catch (Exception e) { observer.onException(e); }}
observer.onCompleted(); return Subscriptions.empty(); } });observable = observable.subscribeOn(Schedulers.executor(mExecutor));observable = observable.observeOn(AndroidScheduler.getInstance());return observable;}
loadEmployeeWithName("Aldo").subscribe(new Observer<Employee>() {
public void onNext(Employee emp) { showEmployeeDataOnScreen(emp); } public void onError(Exception e) { handleException(e); }
public void onCompleted() { doneLoadingEmployees();
}});
Rx (Java): Features
Observer is executed afer someone subscribed Since we are decoupling method call and callback execuKon, we can easily manipulate and combine.
Chain Observables
Observable.concat(obs1, obs2);
Filter Observables
Observable.filter(obs1, new Func1<>() {});
Zip Observables
Observable.zip(obs1, obs2, new Func1<a,b>() {});
Rx (Java): Features
Each observer can be scheduled to have his subscribe happen on any thread, and all observaKon happen on any other thread! Observable<Image> getImage(URL url) {
AtomicBoolean found = new AtomicBoolean(false);obs1 = //get image from in memory cacheobs2 = //get image from disc cacheobs2 = obs2.subscribeOn(Schedulers.executor(highprioexecutor)) .observeOn(AndroidScheduler.getInstance());obs3 = //get image from networkobs3 = obs3.subscribeOn(Schedulers.executor(lowprioexecutor)) .observeOn(AndroidScheduler.getInstance()); return Observable.concat(obs1, obs2, obs3);
}
Rx (Java): Features
Connectable version of Observable can be subscribed mulKple Kmes, doesn't restart but goes on, can even replay past events! Observable<Tweet> obs = createObservableThatStreamTweet();void initObs() {
obs.replay();obs = obs.connect();
}Observable<Tweet> getPopularTweets() {
return obs;}
Reactive the world
• We could wrap all applicaKon in reacKves: click events could generate events that can route to DB calls. View could subscribe to DB observable and...
Yes. Maybe.
Reactive Over Services
<<sync>> Employee DAO
<<sync>> Paypal API
<<async>> Android NFC Intents
RX Layer
UI / Controllers
FragmentA FragmentA AcKvity Views
2ND PROBLEM
Fragments
Fragments
NOT A UI but a LIFECYCLE ABSTRACTION!
Fragments
• OS can create new instances you don't know about
• Retrieve with findByTag()
• Can be a@ached to one of many AcKviKes subclass
Fragments / Communication public class MasterFragment extends Fragment { private Listener listener; public interface Listener { void onItemSelected(int position){} onAttach(Activity a) { super.onAttach(a); listener = (Listener) a;
}}
public class HomeActivity extends Activity implements MasterFragment.Listener {onItemSelected(int position) {
}}
Fragments / Communication
AcKvity1
FragmentA FragmentB
AcKvity2
FragmentC
AcKvity3
FragmentA FragmentD
FragmentC
Fragments / Communication public class HomeActivity extends Activity implements MasterFragment.Listener,DetailFragment.Listener,PopupFragment.Listener {...}}
ListFragment lf = getFragmentManager().findFragmentByTag("LIST");if (lf == null) {getActivity().onItemSelected();
}
Need a Bus
Event Bus
AcKvity1
FragmentA
FragmentB
BUS
AcKvity2
FragmentQ
AcKvity1
Service
Event Bus
AcKvity1
FragmentA
FragmentB
BUS
AcKvity2
FragmentQ
AcKvity1
Service
Excellent library by : OMo
Event Bus
• Also called Publish / Subscribe in distributed systems
• An agent posts a message on an event (topic) • All objects registered on that topic, receive event
• Decouple caller by receiver + synchronous delivery
Otto
bus.post(new AnswerAvailableEvent(42));
bus.register(this);...@Subscribe public void answerAvailable(AnswerAvailableEvent event) { // TODO: React to the event somehow!}...bus.unregister(this);
static Bus bus = new Bus();
Otto
• Event delivered! • O@o is not rocket science:
• ReflecKon to spot methods that declare to handle some object as event
• search for method that accept some event and deliver the event
• More features...
Otto
• Event delivered! • O@o is not rocket science:
• ReflecKon to spot methods that declare to handle some object as event
• search for method that accept some event and deliver the event
• More features...
Remember to call unregister()! Memory (acKvity) leak danger
DIY BUS
DIY Bus
events = integers parameters = object or integers MyObject obj = createMyObject();Bus.i().publish(0x2324, obj);
Bus.i().subscribe(new Invokable() {invoke(int event, Object param) { MyObject obj = (MyObject) param;}
}, 0x2324);
DIY Bus
events = integers parameters = object or integers MyObject obj = createMyObject();Bus.i().publish(0x2324, obj);
Bus.i().subscribe(new Invokable() {invoke(int event, Object param) { MyObject obj = (MyObject) param;}
}, 0x2324);
Bus.java is 90 lines of code!
WRAPPING UP!
BOOST JAVA OBJ-C C# JS you need to work your way towards asynchronous expressiveness
SIMPLE ABSTRACTIONS CAN TURN YOUR WORLD Rx is a simple abstracKon that enables a truckload of features Event Bus is so old, is so simple, and works so well.
SERVER & CLIENT scale is orders of magnitude smaller, different problems, same techniques.
:)
QUESTIONS &
ANSWERS
Links
• RxJava by Nejlix hMps://github.com/NePlix/RxJava/wiki
• O@o by Square hMp://square.github.io/oMo
• DIY Bus hMps://gist.github.com/hazam/8ac4abd927588d0bd04b
• EPFL ReacKve on Coursera hMps://www.coursera.org/course/reacEve
Grazie. Non dimenKcare di riempire il modulo di feedback
AND02 [email protected] hazam emanueledisaverio