5.3
5.35.3 BoundBoundBoundBound ServicesServicesServicesServices
Suppose we want to have a service perform a group of related functions, but do not know until runtime the selection or ordering of those functions. For example, we might want to share some functions across multiple product offerings. The obvious choice for such an implementation would be a service, but until now, we've only seen services that perform a specific task. How would we expose a set of functions that could be run on demand using a service?
The answer is to write a regular service class, including the functions we want to expose in methods of that class. Instead of using startService to run the service, we usebindService. The first time a service is bound to, it starts. Each subsequent call to bindService (from other activities) simply returns abinder that the activity can use to run the service's methods.
Here's an example of a service that can be bound to: package package package packagecom.ebook.service3; import import import importandroid.app.Service; import import import importandroid.content.Intent; import import import importandroid.os.Binder; import import import importandroid.os.IBinder; public public public
public classclassclassclassBoundServiceextendsextendsextendsextendsService { private
privateprivateprivate finalfinalfinalfinalIBinderbinder=newnewnewnewLocalBinder(); public
publicpublicpublic classclassclassclassLocalBinderextendsextendsextendsextendsBinder { BoundService getService() {
return return return
returnBoundService.thisthisthisthis; }
}
@Override public
publicpublicpublicIBinder onBind(Intent arg0) {
//TODOTODOTODOTODOAuto-generated method stub return
returnreturnreturnbinder; }
}
This service does absolutely nothing, but it is the minimal implementation of a bound service. Notice that rather than overriding onCreate, we simply return an instance of an IBinder from the onBind method. The IBinder object is of type LocalBinder, an inner class declaration. This class has a method getService() that returns the bound service itself.
Now let's see how to run the service from the main activity:
public public public
public classclassclassclassMainActivityextendsextendsextendsextendsActivity { BoundServiceservice;
private
privateprivateprivate booleanbooleanbooleanbooleanisBound=falsefalsefalsefalse; private
privateprivateprivateServiceConnectionconn=newnewnewnewServiceConnection() { public
IBinder bService) {
LocalBinder binder = (LocalBinder) bService; service= binder.getService();
isBound=truetruetruetrue; }
public
publicpublicpublic voidvoidvoidvoidonServiceDisconnected(ComponentName className) { isBound=falsefalsefalsefalse;
} };
@Override protected protected
protectedprotected voidvoidvoidvoidonCreate(Bundle savedInstanceState) { super
super
supersuper.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }
protected protected
protectedprotected voidvoidvoidvoidonStart() { super
supersupersuper.onStart();
Intent intent =newnewnewnewIntent(MainActivity.thisthisthisthis, BoundService.classclassclassclass); bindService(intent,conn, Context.BIND_AUTO_CREATE);
}
protected protected
protectedprotected voidvoidvoidvoidonStop() { super
supersupersuper.onStop(); if
ififif(isBound) {
unbindService(conn); isBound=falsefalsefalsefalse; }
} }
For a bound service, any activity that wants to attach to the service must maintain a connection to the service. This is done by instantiating the ServiceConnection class (here as conn). This inner class must override two methods: onServiceConnected and onServiceDisconnected. In our simple implementation, really nothing more than maintaining the state of a boolean (isBound) is done.
When the activity starts, we bind to the service using an intent. The parameters of the bindService method are the intent, the ServiceConnection instance, and a Flag, which is most often set to BIND_AUTO_CREATE, meaning that if the service is not yet started, it should be.
The onStop method first ensures that the service is currently bound. If it is, a call to unbindService (using the ServiceConnection object) is made, and the isBound flag is set false. Now we have a bare – minimal template for creating and running a bound service. Any methods we implement in our service class will be accessible from any other component that binds to the service, through its binder!
For example, we could implement a simple add(int, int) method in the service: public
publicpublicpublic intintintintadd(intintintinta,intintintintb) { return
returnreturnreturna + b; }
and call it from the activity: public
publicpublicpublic voidvoidvoidvoidaddClicked(View v) {
Log.i("bound", String.format("sum: %d",service.add(2, 3))); }
Remember, we must also declare .BoundService in the manifest:
<service
android:name=".BoundService"> </service>
When we run the app, clicking on the Add button results in the log entry: bound sum: 5
It is usual when working with our own bound services to have one activity that controls the lifecycle of the service (by making the first call to bindService, or by starting the service using startService), and allowing other activities to bind to it using onBind. The controlling activity will unbind the service when its onDestroy method is called so the service doesn't just sit
around hogging memory. (The controlling component might also leave the service running so that other application components can access its methods, but this is a recipe for leaked memory unless the service lifecycle is handled very carefully).
Note that a Service can be started, bound to, or even both. There is no difference between a bound service and a normal service. The only difference is the way we attach to the service:
either through startService or bindService. Bound services greatly simplify the process of getting information back from a service, however. Rather than sending information back from a service via an intent, use bound services whenever possible.