: The Command Processor Pattern (Part 1) d.schmidt@vanderbilt.edu www.dre.vanderbilt.edu/~schmidt Professor of Computer Science Institute for Software Integrated Systems Vanderbilt University Nashville, Tennessee, USA
Learning Objectives in this Part of the Module Understand the Command Processor pattern 2
Challenge: Processing a Long-Running Action Context Synchronous method calls in an Activity can block client for extended periods e.g., the downloadimage() call will block the DownloadActivity while the Download fetches the image DownloadActivity Process 1: Activity makes blocking call Download Process 3
Challenge: Processing a Long-Running Action Problems Android generates an Application Not Responding (ANR) dialog if an app doesn t respond to user input within a short time (~3 seconds) Calling a potentially lengthy operation like downloadimage() in the main thread can therefore be problematic DownloadActivity Process 1: Activity makes blocking call Download Process See developer.android.com/training/articles/perf-anr.html 4 for more on ANRs
Challenge: Processing a Long-Running Action Solution Create a command processor that encapsulates a download request as an object that can be passed to a to execute the request Download Activity Command Processor start Download onhandleintent 5
Challenge: Processing a Long-Running Action Solution Create a command processor that encapsulates a download request as an object that can be passed to a to execute the request This process works as follows: Implement a Download that inherits from Android s Intent Download Activity Context start Download onhandleintent Activity Manager start 6
Challenge: Processing a Long-Running Action Solution Create a command processor that encapsulates a download request as an object that can be passed to a to execute the request This process works as follows: Implement a Download that inherits from Android s Intent Activity creates Intent command designating Download as target Add URL & callback Messenger as extras Download Activity Context start Activity Manager start Download onhandleintent 7
Challenge: Processing a Long-Running Action Solution Create a command processor that encapsulates a download request as an object that can be passed to a to execute the request This process works as follows: Implement a Download that inherits from Android s Intent Activity creates Intent command designating Download as target Activity calls start() with Intent Download Activity Context start Activity Manager start Download onhandleintent 8
Challenge: Processing a Long-Running Action Solution Create a command processor that encapsulates a download request as an object that can be passed to a to execute the request This process works as follows: Implement a Download that inherits from Android s Intent Activity creates Intent command designating Download as target Activity calls start() with Intent Activity Manager starts Intent, which spawns internal thread Download Activity Context start Activity Manager start Download onhandleintent 9
Challenge: Processing a Long-Running Action Solution Create a command processor that encapsulates a download request as an object that can be passed to a to execute the request This process works as follows: Implement a Download that inherits from Android s Intent Activity creates Intent command designating Download as target Activity calls start() with Intent Activity Manager starts Intent, which spawns internal thread Download Activity Context start Activity Manager start Download onhandleintent Intent calls onhandleintent() to download image in separate thread See developer.android.com/reference/android/app/intent.html 10 for more
Command Processor Intent POSA1 Design Pattern GoF book contains description of similar Command pattern Packages a piece of application functionality as well as its parameterization in an object to make it usable in another context, such as later in time or in a different thread www.dre.vanderbilt.edu/~schmidt/pdf/commandrevisited.pdf 11 has more info
Command Processor Applicability POSA1 Design Pattern When there s a need to decouple the decision of what piece of code should be executed from the decision of when this should happen e.g., specify, queue, & execute service requests at different times 12
Command Processor Applicability POSA1 Design Pattern When there s a need to decouple the decision of what piece of code should be executed from the decision of when this should happen When there s a need to ensure service enhancements don t break existing code 13
Command Processor Applicability POSA1 Design Pattern When there s a need to decouple the decision of what piece of code should be executed from the decision of when this should happen When there s a need to ensure service enhancements don t break existing code When additional capabilities (such as undo/redo & persistence) must be implemented consistently for all requests to a service 14
Command Processor Structure & Participants POSA1 Design Pattern Intent 15
Command Processor Structure & Participants POSA1 Design Pattern Intent + extras 16
Command Processor Structure & Participants POSA1 Design Pattern Activity 17
Command Processor Structure & Participants POSA1 Design Pattern Intent 18
Command Processor Structure & Participants POSA1 Design Pattern Context 19
Command Processor Dynamics POSA1 Design Pattern Creates the Intent & call send() 20
Command Processor Dynamics POSA1 Design Pattern Intent 21
Command Processor Dynamics POSA1 Design Pattern Call onhandleintent() to process the Intent 22
Command Processor Consequences + Client isn t blocked for duration of command processing POSA1 Design Pattern public void runmessengerdownload(view view) { String url = edittext.gettext().tostring(); Intent intent = new Intent(this, Download.class); intent.setdata(uri.parse (url)); Messenger messenger = new Messenger(handler); intent.putextra("messenger", messenger); } start(intent); Caller doesn t block 23
Command Processor Consequences + Client isn t blocked for duration of command processing + Allow different users to work with service in different ways via commands POSA1 Design Pattern public void onhandleintent(intent intent) { Bundle extras = intent.getextras(); if (extras!= null && extras.get("messenger")!= null) messengerdownload (intent); else broadcastdownload (intent); } 24
Command Processor Consequences Additional programming to handle info passed with commands (cf. Broker) POSA1 Design Pattern public void onhandleintent(intent intent) { Bundle extras = intent.getextras(); if (extras!= null && extras.get("messenger")!= null) messengerdownload (intent); else broadcastdownload (intent); } 25
Command Processor Consequences Additional programming to handle info passed with commands (cf. Broker) Supporting two-way operations requires additional patterns & IPC mechanisms POSA1 Design Pattern void sendpath (String outputpath, Messenger messenger) { Message msg = Message.obtain(); msg.arg1 = result; Bundle bundle = new Bundle(); bundle.putstring(result_path, outputpath); msg.setdata(bundle);... messenger.send(msg); } 26
Command Processor Known Uses Android Intent Many UI toolkits InterViews, ET++, MacApp, Swing, AWT, etc. Interpreters for commandline shells Java Runnable interface POSA1 Design Pattern developer.android.com/reference/android/app/intent.html 27
Summary Command Processor provides a relatively straightforward means for passing commands asynchronously between threads and/or processes in concurrent & networked software www.dre.vanderbilt.edu/~schmidt/pdf/commandrevisited.pdf 28 has more info
: The Command Processor Pattern (Part 2) d.schmidt@vanderbilt.edu www.dre.vanderbilt.edu/~schmidt Professor of Computer Science Institute for Software Integrated Systems Vanderbilt University Nashville, Tennessee, USA
Learning Objectives in this Part of the Module Understand how Command Processor is applied in Android Client 1 start() Intent send intent oncreate() Intent 2 onstartcommand() Handler 3 sendmessage() handlemessage() queue intent MyIntent 4 onhandleintent() 5 dequeue intent process intent 30
Command Processor Implementation Define an abstract class for command execution that will be used by the executor Typically define an execute() operation POSA1 Design Pattern public class Intent implements Parcelable, Cloneable {... } 31
Command Processor Implementation Define an abstract class for command execution that will be used by the executor Add state which concrete commands need during their execution to the context Make the context available to the concrete command POSA1 Design Pattern public class Intent implements Parcelable, Cloneable {... public Intent setdata(uri data) { /*... */ } public Uri getdata() { /*... */ } public Intent putextra (String name, Bundle value) {/*... */ } } public Object getextra (String name) {/*... */ } 32
Command Processor Implementation Define an abstract class for command execution that will be used by the executor Add state which concrete commands need during their execution to the context Define & implement the creator e.g., using patterns like Abstract Factory or Factory Method POSA1 Design Pattern public class DownloadActivity extends Activity {... public void onclick(view v) { Intent intent = new Intent(DownloadActivity.this, Download.class);... intent.setdata (Uri.parse(userInput);... start(intent); }... 33
Command Processor Implementation Define an abstract class for command execution that will be used by the executor Add state which concrete commands need during their execution to the context Define & implement the creator Define the context If necessary allow it to keep references to command objects, but be aware of lifecycle issues POSA1 Design Pattern public abstract class Context { public abstract void sendbroadcast(intent intent); public abstract Intent registerreceiver (BroadcastReceiver receiver, IntentFilter filter); 34
Command Processor Implementation Define an abstract class for command execution that will be used by the executor Add state which concrete commands need during their execution to the context Define & implement the creator Define the context Implement specific command functionality in subclasses: Implementing the execute() operation, adding necessary attributes POSA1 Design Pattern public class Download extends Intent { }... protected void onhandleintent(intent intent) { downloadimage(intent); }... 35
Command Processor POSA1 Design Pattern Applying Command Processor in Android (Some) steps involved in the Android implementation of Command Processor pattern DownloadActivity Process Download Process DownloadActivity mmessenger start() 5: Return URI 1: Sent Intent to Activity Manager 2: Activity Manager starts the Download if it s not already running Activity Manager Download onhandleintent() 4: Download image & reply via Messenger 3: Intent base class queues the Intent & calls onhandleintent(), which runs in a separate thread Other patterns are involved here: Activator, 36 Messaging, Result Callback, etc.
Command Processor Applying Command Processor in Android (Portion of) the DownloadActivity implemented using the Command Processor pattern public void rundownloadimage(view view) { URL url = new URL(image_url.getText().toString()) Intent intent = new Intent(this, Download.class); Make Intent command POSA1 Design Pattern intent.putextra(download.messenger, new Messenger(handler)) intent.putextra(download.url, url); } start(intent); Issue request to command processor This code runs in the DownloadActivity 37 process
Command Processor Applying Command Processor in Android POSA1 Design Pattern (Portion of) the Download implemented using the Command Processor pattern Command processor executes the request public class Download extends Intent {... protected void onhandleintent(intent intent) { Bundle extras = intent.getextras(); URL url = (URL)extras.get(URL); Messenger messender (Messenger)extras.get(MESSANGER); } } // Download image at designated URL & send // reply back to Activity via messenger callback downloadimage(url, messenger); This code runs in a thread in 38 the Download process
Summary The Android Intent framework implements the Command Processor pattern & is used to process Intents in a background Thread Client 1 start() Intent send intent oncreate() Intent 2 onstartcommand() Handler 3 sendmessage() handlemessage() queue intent MyIntent 4 onhandleintent() 5 dequeue intent process intent 39
: Overview of Bound s d.schmidt@vanderbilt.edu www.dre.vanderbilt.edu/~schmidt Professor of Computer Science Institute for Software Integrated Systems Vanderbilt University Nashville, Tennessee, USA
Learning Objectives in this Part of the Module Understand how what a Bound is & what hook methods it defines to manage its various lifecycle states We ll emphasize commonalities & variabilities in our discussion 41
Interfacing with a Bound A Bound offers components an interface that clients can use to interact with the This interface can be generic e.g., using Messengers & Handlers for inter- or intra-process communication Messenger send() Handler handlemessage() Download Activity Download 42
Interfacing with a Bound A Bound offers components an interface that clients can use to interact with the This interface can be generic This interface can also be specific e.g., using the Android Interface Definition Language (AIDL) for inter- or intra-process communication interface IDownload { String downloadimage(in Uri uri); } Download Activity Download 43
Interfacing with a Bound A Bound offers components an interface that clients can use to interact with the This interface can be generic This interface can also be specific Both approaches use the Binder RPC mechanism This implements the Broker & Proxy patterns Download Activity Download 44
Launching a Bound A Bound allows App components to bind to it by calling bind() to create a persistent connection Intent intent = new Intent(IDownloadSync.class.getName()); bind(intent, this.conn, Context.BIND_AUTO_CREATE); Download Activity Download developer.android.com/guide/components/services.html#creatingbound 45
Launching a Bound A Bound allows App components to bind to it by calling bind() to create a persistent connection The client must provide Connection object to monitor the connection with the Intent intent = new Intent(IDownloadSync.class.getName()); bind(intent, this.conn, Context.BIND_AUTO_CREATE); Download Activity Download 46
Connecting a Bound When the client calls bind() Android starts the & invokes the s oncreate() & onbind() hook methods If the isn t already running it will be activated IDownload.Stub binder; Class Download extends { public void oncreate(bundle savedinstance) { binder = new IDownload.Stub() { public String downloadimage(string urlvalue) { /*... */ } } Download Activity... Download See www.dre.vanderbilt.edu/~schmidt/pdf/activator.pdf 47 for info on Activator
Connecting a Bound When the client calls bind() Android starts the & invokes the s oncreate() & onbind() hook methods If the isn t already running it will be activated onbind() returns an IBinder object that defines the API for communicating with the Bound... public IBinder onbind(intent intent) { return this.binder; } Download Activity Download An interesting callback-driven protocol 48 is used to establish a connection
Connecting a Bound When the client calls bind() Android starts the & invokes the s oncreate() & onbind() hook methods The client needs to implement an onconnected() hook method to get a proxy to the IBinder Idownload syncsvc; Connection conn = new Connection() { public void onconnected (ComponentName classname, IBinder isvc) { syncsvc = IDownload.Stub.asInterface(iSvc); } Download Activity Download 49
Interfacing with a Bound A Bound offers components an interface that clients can use to interact with the, e.g.: Sending requests Getting results & Conversing across processes via IPC final String pathname = syncsvc.downloadimage(url); Download Activity Download Blocking call 50
Interfacing with a Bound A Bound offers components an interface that clients can use to interact with the, e.g.: Sending requests Getting results & Conversing across processes via IPC IDownload.Stub binder = new IDownload.Stub() { public String downloadimage(string url) { return downloadimagefromurl(url, DOWNLOADED_FILE_NAME_PREFIX); } Download Activity Download Run in a separate thread of control 51
Stopping a Bound When a Bound is launched, it has a lifecycle that's depends of the component(s) that access it i.e., it doesn t run in the background indefinitely When a client is done interacting with the service, it calls unbind() to unbind After there are no clients bound to the service it is destroyed Download Activity Download developer.android.com/guide/components/bound-services.html#lifecycle 52
It is possible to define hybrid models that combine Bound & Started s If a Bound implements onstartcommand() it won t be destroyed when it is unbound from all clients Hybrid Started & Bound Lifecycles developer.android.com/guide/components/bound-services.html#lifecycle 53
It is possible to define hybrid models that combine Bound & Started s If a Bound implements onstartcommand() it won t be destroyed when it is unbound from all clients If you return true when the system calls onunbind() the onrebind() method will be called the next time a client binds to the Instead of receiving a call to onbind() Hybrid Started & Bound Lifecycles developer.android.com/guide/components/bound-services.html#lifecycle 54
Protocol for Bound Interactions A protocol is used to interact between Activities & Bound s Client Process Server Process Binder IPC Mechanism 5: Assign Proxy to local variable Activity mdosomething bind() Connection onconnected() 6: Call the method dosomething() via Proxy 1: Bind to 2: Start Bound process if it s not already running 4: Downcast to Proxy mbinder onbind() ISomeBinder dosomething() 7: Stub dispatches to method & returns result 3: Return reference to object that implements the IBinder interface Many patterns are involved here: Broker, 55 Proxy, Activator, Adapter, etc.
Client Bound Interactions To bind to a service from your client, you must perform the following steps: Client Process Activity mdosomething bind() Connection onconnected() ondisconnected() 1. Implement Connection & override its two callback methods: onconnected() Android calls this to deliver the IBinder returned by the service's onbind() method ondisconnected() Android calls this when the connection to the service is unexpectedly lost e.g., when the service has crashed or has been killed (not called with client calls unbind()) 56
Client Bound Interactions To bind to a service from your client, you must perform the following steps: : Assign Proxy to local variable Client Process Activity mdosomething bind() Connection 1. Implement Connection & override its two callback methods 2. Call bind(), passing the Connection implementation onconnected() ondisconnected() 57
Client Bound Interactions To bind to a service from your client, you must perform the following steps: Client Process Activity mdosomething bind() Connection 1. Implement Connection & override its two callback methods 2. Call bind(), passing the Connection implementation 3. When the system calls your onconnected() callback method, you can begin making calls to the service, using the methods defined by the interface onconnected() ondisconnected() 58
Client Bound Interactions To bind to a service from your client, you must perform the following steps: Client Process Activity mdosomething unbind() Connection onconnected() ondisconnected() 1. Implement Connection & override its two callback methods 2. Call bind(), passing the Connection implementation 3. When the system calls your onconnected() callback method, you can begin making calls to the service, using the methods defined by the interface 4. To disconnect from the service, call unbind() When a client is destroyed, it is unbound from the automatically 59
Client Bound Interactions To bind to a service from your client, you must perform the following steps: Client Process Activity mdosomething unbind() Connection onconnected() ondisconnected() 1. Implement Connection & override its two callback methods 2. Call bind(), passing the Connection implementation 3. When the system calls your onconnected() callback method, you can begin making calls to the service, using the methods defined by the interface 4. To disconnect from the service, call unbind() Always unbind when you're done interacting with the service or when your activity pauses so that the service can shutdown while its not being used 60
Server Bound Interactions A Bound must perform the following steps when a client binds to it: When Android calls the 's onbind() Server Process method it returns an IBinder for interacting with the mbinder onbind() ISomeBinder 3: Return reference to object that implements the IBinder interface dosomething() 61
Server Bound Interactions A Bound must perform the following steps when a client binds to it: When Android calls the 's onbind() Server Process method it returns an IBinder for interacting with the The binding is asynchronous mbinder i.e., bind() returns immediately & does not return onbind() the IBinder to the client ISomeBinder dosomething() 62
Server Bound Interactions A Bound must perform the following steps when a client binds to it: When Android calls the 's onbind() Server Process method it returns an IBinder for interacting with the The binding is asynchronous mbinder To receive the IBinder, the client must create an instance of onbind() Connection & pass it to bind() ISomeBinder Connection implements dosomething() the onconnected() callback method that Android invokes to deliver the IBinder 63
Communicating with Bound s When creating a Bound, you must provide an IBinder via an interface clients can use to interact with the via one of the following Client Process Activity Extending the Binder class If your service runs in the same process as the client you can Server Process mdosomething extend the Binder class & return an instance mbinder from onbind() bind() onbind() Connection onconnected() ISomeBinder dosomething() 64
Communicating with Bound s When creating a Bound, you must provide an IBinder via an interface clients can use to interact with the via one of the following Client Process Extending the Binder class Server Process Using a Messenger Activity Create an interface for the mdosomething mbinder with a Messenger that allows the client to send bind() Connection commands to the across processes Doesn t require thread-safe components onbind() ISomeBinder onconnected() dosomething() 65
Communicating with Bound s When creating a Bound, you must provide an IBinder via an interface clients can use to interact with the via one of the following Client Process Extending the Binder class Server Process Using a Messenger Activity Using Android Interface mdosomething mbinder Definition Language (AIDL) bind() Connection AIDL performs all the work to decompose objects into primitives that Linux can understand & marshal them onbind() ISomeBinder onconnected() across processes to perform IPC Does require thread-safe components dosomething() 66
Summary Apps can use s to implement longrunning operations in the background Download Activity Download 67
Summary Apps can use s to implement longrunning operations in the background Started s are simple to program Download Activity Download 68
Summary Apps can use s to implement longrunning operations in the background Started s are simple to program Bound s provide more powerful communication models Download Activity Download 69
Summary Apps can use s to implement longrunning operations in the background Started s are simple to program Bound s provide more powerful communication models Examples of Android Bound s: BluetoothHeadset Provides Bluetooth Headset & Handsfree as in Phone App MediaPlayback Provides "background" audio playback capabilities Exchange Email s Manage email operations, such as sending messages See packages/apps in Android 70 source code for many services