Your SlideShare is downloading. ×
Mattbrenner
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Mattbrenner

586
views

Published on

Published in: Technology, Business

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
586
On Slideshare
0
From Embeds
0
Number of Embeds
5
Actions
Shares
0
Downloads
12
Comments
0
Likes
0
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. A Unified Approach to Callbacks in Android Matt Brenner – UnME2, Inc. mbrenner@unme2.com Droidcon Berlin 2013
  • 2. Background KnowledgeThis talk will include discussion of, threads Interfaces callbacks Observer Design Patternand make extensive use of UML.Dont worry, well “fill in” all gaps along the way!
  • 3. A Unified Approach to Callbacks in AndroidIn Java, callbacks commonly occur within processes: between threads to decouple domain / interface (e.g. Observer)
  • 4. “Worker Thread” Callbackpublic Interface Requester { public void complete ( ); Requester} «interface»public class Mainclass implements Requester { + complete( ) : void ... public void dosomething (...) { ... (new Worker (this)).start (); } Mainclass public void complete ( ) { + dosomething (...) : void ... + complete( ) : void } ...}public class Worker extends Thread { private Requester requester_; Thread ... public Worker (Requester requester) { requester_ = requester } ... public void run( ) { Worker ... requester_.complete ( ); + Worker (requester : Requester) } + run ( ) : void} ...
  • 5. “Domain-to-Interface” Callback (Observer Pattern) Observerpublic class Widget implements Observer { «interface» ... public Widget (Domainthing thing) { + update (:Observable, data : Object) : void ... thing.addObserver (this); } public void update (Observable obs, Object Widgetdata) { ... + Widget (:Domainthing) } + update (:Observable, data : Object) : void} ...public class Domainthing extends Observable { Observable ... private void domainchange (Object data ) { + addObserver (:Observer) : void + setChanged ( ) : void ... setChanged ( ); + notifyObservers (:Object) : void notifyObservers (data); }} Domainthing
  • 6. Android Makes Extensive Use IntentsIntents are used: for inter-process and inter-Activity comms to invoke Activities (passing params) to callback from one Activity to another (return a result)
  • 7. Activity Callback(Calling Activity)public class AnActiviy extends Activity { static private final int GOTYESNO = 1, ... public void askyesno( ) { Intent intent; ... intent = new Intent (this, ActivityGetyesno.class); intent.putExtra (“title”, R.string.delete_all_title); intent.putExtra (“msg”, R.string.delete_all_msg); startActivityForResult (intent, GOTYESNO); } protected void onActivityResult (int id, int result, Intent data) { if (id == GOTYESNO) { if (data.hasExtra (“RESULT”)) { result = data.getExtras( ).getString(“RESULT”); if (result.equals (“yes”)) { ... } } } } ...}
  • 8. Activity Callback(Called Activity)public class ActivityGetyesno extends Activity { ... protected void onCreate (Bundle bundle) { super.onCreate (bundle); String title, message; title = message = null; Bundle params = getIntent ( ).getExtras( ); if (params != null) { title = params.getString (“title”); message = params.getString(“msg”); } ... } public void clickno (View view) { done (“no”); } public void clickyes (View view) { done (“yes”); } private void done (String result) { Intent intent = new Intent ( ); intent.putExtra (“RESULT”, result); setResult (Activity.RESULT_OK, intent); finish ( ); }
  • 9. A Bit of a Mess Multiple callbacks in various forms lead to: interface “clutter” multiple callback forms in a single class Intent syntax is klunky and repetitiousNew design pattern to the rescue: Event Consumer (Brenner)
  • 10. Event Consumer Activity SuperActivity «abstract» Eventconsumer «interface»+ event (eventid : int) : void+ event (eventid : int, data : Object) : void + event (eventid : int) : void+ event (eventid : int, data : Object, resultcode : int) : void + event (eventid : int, data : Object+ onActivityResult (eventid : int, resultcode : int, intent : Intent) : void# abort ( ) : void# done (response : int) : void# done (response : String) : void# getparamstring ( ) : String# getresult ( ) : int# getresultstring ( ) : String# isabort ( ) : boolean# isuccess ( ) : boolean# isyes ( ) : boolean AnActivity + func ( ) : void + event (eventid : int, data : Object) : void DialogActivity «abstract»+ onCreate (bundle: Bundle, layout : int) : void+ event (eventid : int, data : Object) : void+ getitlefield ( ) : TextView+ getmsgfield ( ) : TextView+ onBackPressed ( ) : void ActivityGetyesnodialog + onCreate (bundle : Bundle) : void + clickno (View view) : void + clickyes (View view) : void
  • 11. Using the Event Consumer PatternLets see how to use Event Consumer for callbacks: from a worker-thread from domain to interface between Intents
  • 12. Event Consumer: “Worker Thread” CallbackOld Version (caller)New Version (caller) NewVersion (worker) Old Version (worker) Interface Requester {public class Mainclass public class Worker extends Thread { public void complete ( ); implements Eventconsumer Requester private int requester_; eventid_;}{ ... static private final int COMPLETE = 1; privateWorker (Requester requeseter) { public Eventconsumer caller_;public class Mainclass implements Requester { ... requester_ = requeseter; ... } public Worker (Eventconsumer caller, int eventid) { public void dosomething (...) { ... caller_ = caller; ... (new Worker (this, COMPLETE)).start ( public void run( = {eventid; eventid_ )); (new Worker (this).start (); } ... } requester_.complete( ); } public void run ( ) { public void complete ( ) { } ... ... public void event (int eventid, Object data) { caller_.event (eventid_); } switch (eventid) { }} case COMPLETE: } ... break; } }}
  • 13. Event Consumer: “Domain-to-Interface” CallbackNew Version Old Versionpublic class Widget Old Version public class Widget implements Observer { implements Eventconsumer ... public class Domainthing extends Observable {{ ... static private final int UPDATE = 1; private void domainchange (Object data ) { ... ... public Widget (Domainthing thing) { setChanged ( ); public void observe (Domainthing thing) { ... notifyObservers (data); thing.addobserver (this, UPDATE); thing.addObserver (this); } } } } public void event (int eventid, Object data) { public void update (Observable obs, Object data) { switch (eventid) { ... case UPDATE: } ... New Version } break; public class Domainthing { } private int eventid_; } private Eventconsumer observer_;} ... public void addobserver (Eventconsumer obs, int eventid) { observer_ = obs; eventid_ = eventid; } private void domainchange (Object data) { ... observer_.event (eventid, data); } }
  • 14. Event Consumer: Intent CallbackNew Version (Calling Activity) Old Version (Calling Activity)public class AnActiviy extends SuperActivity { public class AnActiviy extends Activity { static private final int GOTYESNO = 1; static private final int GOTYESNO = 1, ... ... public void askyesno( ) { public void askyesno( ) { Launcher launcher; Intent intent; ... ... lancher = new Launcher (this, GOTYESNO, intent = new Intent (this, ActivityGetyesno.class); R.string.delete_all_title, intent.putExtra (“title”, R.string.delete_all_title);R.string.delete_all_msg); intent.putExtra (“msg”, R.string.delete_all_msg); startActivityForResult (intent, GOTYESNO); launcher.launch (ActivityGetyesno.class); ... } } protected void onActivityResult (int id, int result,Intent data) public void event (int eventid, Object data) { { switch (eventid) { if (id == GOTYESNO) { case GOTYESNO: if (data.hasExtra (“RESULT”)) { if (isyes()) { result = data.getExtras( ).getString ... (“RESULT”); } if (result.equals (“yes”)) { break; ... } } } }} } } }
  • 15. Event Consumer: Intent CallbackNew Version (Called Old Version (Called Activity)Activity) ActivityGetyesnopublic class public class ActivityGetyesno extends Activity { extends DialogActivity ...{ protected void onCreate (Bundle bundle) { public void onCreate (Bundle bundle) { super.onCreate (bundle); super.onCreate (bundle,R.layout.dialog_yesno); String title, message; ... } title = message = null; Bundle params = getIntent ( public void clickno (View view) { ).getExtras ( ); done (NO); if (params != null) { } title = params.getString (“title”); public void clickyes (View view) { message = done (YES); params.getString (“msg”); } }} ... } public void clickno (View view) { done (“no”); } public void clickyes (View view) { done (“yes”); } private void done (String result) { Intent intent = new Intent ( ); intent.putExtra (“RESULT”, result); setResult (Activity.RESULT_OK, intent);
  • 16. Helper Class for Intent Callbacks: LauncherLauncher encapsulates the gory details of creating and firing Intents many convenient constructors manages parameters passing creates Intent launch method invokes: startActivity (...) or startActivityForResult (...) Launcher + Launcher (: Activity, eventid : int) + Launcher (: Activity, param : String) + Launcher (: Activity, eventid : int, param : String) + Launcher (: Activity, title : int, msg : int) + Launcher (: Activity, title : String, msg : String) + Launcher (: Activity, eventid : int, title : int, msg : int) + Launcher (: Activity, eventid : int, title : int, msg : int, list : String[ ]) + Launch (cls : Class<?>) : void + setgravity (gravity : int) : void + setmaxdigits (: int) : vod + setmindigits (: int) : void + setmultiline( ) : void + setparam (key : String, value : String) : void + trapabort( ) : void
  • 17. public class AnActiviy extends SuperActivity { static private final int COMPLETE = 1, UPDATE = 2,Voila! A Unified Approach to Callbacks: GOTYESNO = 3; ... Worker Thread public void dosomething (...) { Domain-to-Interface (new Worker (this, COMPLETE)).start ( ); } Intent-to-Intent public void observe (Domainthing thing) { thing.addobserver (this, UPDATE); } public void asktyesno( ) { Launcher launcher; ... lancher = new Launcher (this, GOTYESNO, R.string.delete_all_title, R.string.delete_all_msg); launcher.launch (ActivityGetyesnodialog.class); } public void event (int eventid, Object data) { switch (eventid) { case COMPLETE: // thread-to- thread callbackevent ... break; case UPDATE: // domain-to-interface callback event ... break; case GOTYESNO: // intent-to-intent callback event if (isyes()) { ... } break; } } }
  • 18. An Unsatisfying AspectI commonly apply Eventconsumer to Activities: extend Superactivity implemement event methodIt is natural to apply it to ListActivity too: create SuperListActivity must duplicate (or delegate) methods similar to those in SuperActivity
  • 19. Event Consumer Activity SuperActivity «abstract» Eventconsumer «interface»+ event (eventid : int) : void+ event (eventid : int, data : Object) : void + event (eventid : int) : void+ event (eventid : int, data : Object, resultcode : int) : void + event (eventid : int, data : Object+ onActivityResult (eventid : int, resultcode : int, intent : Intent) : void# abort ( ) : void# done (response : int) : void# done (response : String) : void# getparamstring ( ) : String# getresult ( ) : int# getresultstring ( ) : String# isabort ( ) : boolean# isuccess ( ) : boolean# isyes ( ) : boolean AnActivity + func ( ) : void + event (eventid : int, data : Object) : void DialogActivity «abstract»+ onCreate (bundle: Bundle, layout : int) : void+ event (eventid : int, data : Object) : void+ getitlefield ( ) : TextView+ getmsgfield ( ) : TextView+ onBackPressed ( ) : void ActivityGetyesnodialog + onCreate (bundle : Bundle) : void + clickno (View view) : void + clickyes (View view) : void