Right now I'm working on my diploma thesis and have to play around with android services.
Part of my application needs the ability to bind multiple services that all implement the same interface.
Why? Well it's some kind of the Strategy Pattern.
Think of one problem, and multiple solutions, each with different characteristics. For example a complex computation. For one request there could be different algorithms with different properties like computationtime or precision.
It's also some kind of implementing the SOA Paradigma (service Oriented Architecture) like with Soap WebServices.
Extensibility is very important. So what I want is that my main-application (the serviceclient) provides an interface (via AIDL) And based on that, different applications (especially services) could be created (from and published by other people like you for example) that implement this interface and do the work.
Now when the main application is running, it in some way detects which services are available for the interface and selects -> binds one or more of them and uses it's functions.
So summarized the problem is: detecting and binding multiple services for the same interface.
- create the interface
- implement the interface in different services
- make the services detectable
- find a way to dynamically detect which such services are available on the device.
- retrieve some kind of service description for each service (to be able to decide which service we actually want to use)
- select one ore more services and bind them for an activity/service
What I've discovered so far is more kind of the contrary thing: multiple activities binding to the same service. But no approach like this.
So I decided to post here my experience and results while trying to solve the problem.
Maybe you like it and find it useful or have other ideas how certain steps could be realized or improved.
For the tests we have 3 single applications
- the main app "ipctest serviceclient" that wants to bind to the other services and provides the interface
- several service-apps ("ipctest service one", "ipctest service two") that contain a service class, implementing the interface
1. Provide the interface.
This is straightforward. Just create a new AIDL-File
For the example it only has one method, that is supposed to return a string containing the identity of the implementing service
2. Implement the interface
To make implementation easier I decided to write an abstract class from which service implementations should extend
Now we create new services, extending the abstract implementation for the interface.
3. make the services detectable
To make the serviceimplementations runnable and detectable we have to declare them in the android manifest.
Inside the [font=Courier New]<application>[/font] we declare the service:
Note the intent-filter that specifies the name of the interface we implement as action. In this way every service that implements this interface reacts to the same intent - cool!
4. detect available services
So far so good.
Now we want dynamically detect all services that implement our interface, in fact before actually binding to them.
How we gonna do it:
First step we define an intent with the name of the interface as action like we declared in the services manifest. Then we a query the packagemanager to get a ResolveList, containing infos about our services.
(Next code-snippets are placed in the onCreate() in the main activity of my testapplication (ipctest serviceclient)
Now we could for example iterate through the [font=Courier New]ResolveList[/font] and print some infos about the found services into a textview:
5. Binding to multiple services
Now comes the tricky part. We do not only want to bind to ONE of the just discovered services, we want to bind to ALL of them.
How we gonna do it?
For one service we need an instance of type [font=Courier New]IIpcTestService[/font] which we retrieve through a ServiceConnection.
For multiple services we need a whole collection of service instances.
In our main-activty we need a map for the serviceinstances and a map for the ServiceConnections, the key always refers to the full qualified name of the service-class:
In the onCreate, after getting the ResolveList (see 5.) wie iterate through the list and call bindService
You may ask why we need more than one ServiceConnection instance. In the beginning I used only one and it worked. I could bind both services with one single instance. But when it comes to UNbind them it becomes complicated. To unbind a service you call [font=Courier New]unbindService(ServiceConnection);[/font] You can't specify WHICH service you want to unbind, only the ServiceConnection-Object is passed. But having all services bound with ONE ServiceConnection, all services are unbound at once. So I guess we need different ServiceConnections - one for each Service.
Here is what the RemoteServiceConnection class looks like:
To avoid leaking serviceconnection don't forget to unbind them before your activity gets killed:
And that's it.
It's not very convenient at this early stage, but it works.
I hope the codefragments gave you the idea of what I intended to do.
If necessary I could upload the sources of the testapplications
Another interesting thing I noticed: when you try to bind a service using an implizit intent and there are more than one services that match it, android doesnt ask you which to bind, it just binds the first one. Unlike with activities, where the user is asked for decision.
Next steps to do:
- create some kind of service-description and evaluate it after getting the ResolveList. I think best way to do this is to add some meta-data in the manifest for each service implementation. To get meta-data we need to set the flag GET_META_DATA when querying the PackageManager:
[font=Courier New]List<ResolveInfo> list = pm.queryIntentServices(interfaceIntent,pm.GET_META_DATA);[/font]
I'm working and testing on it...
- create a RemoteServiceManager class that manages the discovery, selection (based on some servicedescription) and binding of the services. All what we have done in the example-activity should be done by a manager class for easier use.
- maybe I create a new class that holds the stuff for a remoteservice: status (bound or unbound) servicestub, the connection, servicedescription...