ASYNCHRONOUS TECHNIQUES FOR ANDROID: PROGRAMMING SIDEWAYS Emanuele Di Saverio AND02 Senior Design Technologist [email protected] hazam emanueledisaverio
Aug 23, 2014
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