• No results found

5.3 5.3 Bound Bound Bound Bound Services Services Services Services

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.