programming sideways: asynchronous techniques for android

Post on 23-Aug-2014

289 Views

Category:

Mobile

1 Downloads

Preview:

Click to see full reader

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  

emanuele.disaverio@gmail.com  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 emanuele.disaverio@gmail.com  hazam  emanueledisaverio  

top related