O slideshow foi denunciado.
Seu SlideShare está sendo baixado. ×

Programming Sideways: Asynchronous Techniques for Android

Programming Sideways: Asynchronous Techniques for Android

Baixar para ler offline

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)

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)

Anúncio
Anúncio

Mais Conteúdo rRelacionado

Anúncio
Anúncio

Livros relacionados

Gratuito durante 30 dias do Scribd

Ver tudo

Audiolivros relacionados

Gratuito durante 30 dias do Scribd

Ver tudo

Programming Sideways: Asynchronous Techniques for Android

  1. 1. ASYNCHRONOUS TECHNIQUES FOR ANDROID: PROGRAMMING SIDEWAYS Emanuele  Di  Saverio   AND02                    Senior  Design  Technologist   emanuele.disaverio@gmail.com   hazam   emanueledisaverio  
  2. 2. Who? "Agilist  in  a  Mobile  world"   •  Mobile  Java  Developer  (2007)   •  Android  (2009)   •  h@p://www.androidavanzato.it  (2012)   Emanuele  Di  Saverio   Senior  Design  Technologist  @  
  3. 3. TWO RECURRING PROBLEMS, WITH SIMILAR SYMPTOMS
  4. 4. 1ST PROBLEM
  5. 5. A (biased) TALE ABOUT SERVERS
  6. 6. The S in C/S •  a  server  program  in  (connected)  network  compuKng   while (true) { connect(); read(); parseRequest(); process(); sendResponse(); disconnect(); }
  7. 7. MPM Prefork (Apache) •  let's   fork() and  spawn  more  processes!   full  address  space  copied  around  
  8. 8. MPM Worker (Apache) •  handful  processes  spawn  a  set  of  threads   context  switch  +  shared  memory  
  9. 9. Multi Threaded Programming •  Memory  is  shared,  threads  spawn  faster   •  Problem  of  synchronizaKon  –  solvable  only   with  low-­‐level  OS  primiKves  
  10. 10. 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  
  11. 11. CONTEXT SWITCH has  fixed  cost.  
  12. 12. Context Switch
  13. 13. Context Switch hint:  human  mind  work  same  way  
  14. 14. Single Thread Event Old  technique  –  New  Trend   Nginx / NodeJS / Jetty •  User  Code  is  executed  by  single   thread   •  I/O  is  on  separate  thread  
  15. 15. MOBILE UI ARCHITECTURE a.k.a.  looked  from  a  high  enough  level,  they're  all  the  same.    
  16. 16. The Loop while (true) { processInputEvents(); processSysEvents(); measure(); layout(); draw(); } input   sys   measure   layout   draw   thread  
  17. 17. 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(){...} }
  18. 18. The Android Loop All  callbacks  are  executed  in  Main   Thread!   •  AcKvity   •  Service   •  Fragment   •  animaKons   •  OnClicks   •  OnTouch   •  OnWhatever...      
  19. 19. 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));
  20. 20. 60 FPS -> 16 ms per frame ALL YOUR CODE NEEDS TO FIT IN 16ms
  21. 21. The Loop while (true) { processInputEvents(); processSysEvents(); measure(); layout(); draw(); } input   sys   measure   layout   draw   thread   16ms  
  22. 22. DON'T BLOCK UI THREAD no  network  calls  -­‐  no  file  I/O  -­‐  no  intensive  computaKon  
  23. 23. DON'T BLOCK UI THREAD no  network  calls  -­‐  no  file  I/O  -­‐  no  intensive  computaKon   USE OTHER THREADS!
  24. 24. 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
  25. 25. 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 } });
  26. 26. 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
  27. 27. 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()));
  28. 28. 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(); }});
  29. 29. 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(); } }
  30. 30. GET() BLOCKS COMBINATION IS A PAIN
  31. 31. 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); }
  32. 32. Callback fetchNetworkContentAsync(Callback<Response> cb) { mExecutor.execute(new Runnable(){ ... try { Response r = fetchNetworkContent(); cb.done(r, null); } catch(Exception exc) { cb.done(null, exc); }); }
  33. 33. WAIT, WE NEED TO BE IN MAIN THREAD
  34. 34. 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); }); }
  35. 35. 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);
  36. 36. 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); });
  37. 37. ANDROID SDK HELP
  38. 38. 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  
  39. 39. AsyncTask
  40. 40. NEED MORE ASYNC EXPRESSIVENESS a.k.a.  my  head  is  hurKng  
  41. 41. ENTER REACTIVE EXTENSIONS
  42. 42. 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  
  43. 43. 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      
  44. 44. 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(); }});
  45. 45. "Async" expressed for collections IMPERATIVE   •  T  next()   •  throws  ExcepKon   •  return   REACTIVE   •  onNext(T)   •  onError(ExcepKon)   •  onCompleted()  
  46. 46. 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  
  47. 47. 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;}
  48. 48. 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    
  49. 49. 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; }
  50. 50. 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); }
  51. 51. Rx (Java): Features Observer  is  executed  afer  someone  subscribed     Since  we  are  decoupling  method  call  and  callback   execuKon,  we  can  easily  manipulate  and  combine.  
  52. 52. Chain Observables Observable.concat(obs1,  obs2);  
  53. 53. Filter Observables Observable.filter(obs1,  new  Func1<>()  {});  
  54. 54. Zip Observables Observable.zip(obs1,  obs2,  new  Func1<a,b>()  {});  
  55. 55. 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 cache obs2 = //get image from disc cache obs2 = obs2 .subscribeOn(Schedulers.executor(highprioexecutor)) .observeOn(AndroidScheduler.getInstance()); obs3 = //get image from network obs3 = obs3 .subscribeOn(Schedulers.executor(lowprioexecutor)) .observeOn(AndroidScheduler.getInstance()); return Observable.concat(obs1, obs2, obs3); }
  56. 56. 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; }
  57. 57. 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.  
  58. 58. Reactive Over Services <<sync>>   Employee  DAO   <<sync>>   Paypal  API   <<async>>   Android  NFC  Intents   RX  Layer   UI  /  Controllers   FragmentA   FragmentA  AcKvity   Views  
  59. 59. 2ND PROBLEM
  60. 60. Fragments
  61. 61. Fragments NOT A UI but a LIFECYCLE ABSTRACTION!
  62. 62. Fragments •  OS  can  create  new   instances  you  don't   know  about   •  Retrieve  with   findByTag()   •  Can  be  a@ached  to   one  of  many  AcKviKes   subclass  
  63. 63. 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) { }}
  64. 64. Fragments / Communication AcKvity1   FragmentA   FragmentB   AcKvity2   FragmentC   AcKvity3   FragmentA   FragmentD   FragmentC  
  65. 65. Fragments / Communication public class HomeActivity extends Activity implements MasterFragment.Listener, DetailFragment.Listener, PopupFragment.Listener { ... }} ListFragment lf = getFragmentManager().findFragmentByTag("LIST"); if (lf == null) { getActivity().onItemSelected(); }
  66. 66. Need a Bus
  67. 67. Event Bus AcKvity1   FragmentA   FragmentB   BUS   AcKvity2   FragmentQ   AcKvity1   Service  
  68. 68. Event Bus AcKvity1   FragmentA   FragmentB   BUS   AcKvity2   FragmentQ   AcKvity1   Service  
  69. 69. 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  
  70. 70. 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();
  71. 71. 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...  
  72. 72. 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  
  73. 73. DIY BUS
  74. 74. 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);
  75. 75. 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!    
  76. 76. WRAPPING UP!
  77. 77. BOOST JAVA OBJ-C C# JS you  need  to  work  your  way  towards  asynchronous  expressiveness  
  78. 78. 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.  
  79. 79. SERVER & CLIENT scale  is  orders  of  magnitude  smaller,     different  problems,   same  techniques.  
  80. 80. :)
  81. 81. QUESTIONS& ANSWERS
  82. 82. 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  
  83. 83. Grazie.Non  dimenKcare  di  riempire  il  modulo  di  feedback   AND02 emanuele.disaverio@gmail.com   hazam   emanueledisaverio  

×