Bound services are services that allow users to bind themselves to them and ask for information. The examples of bound services can be seen in the location, and sensor services are natively run on modern Android phones. The principal addition of the bound service in comparison to the started service is its ability to accept clients' requests. The clients bind themselves to services using an IBinder
instance. In this recipe, we will see how to implement a bound service and its Binder
.
To follow this recipe, create a new project in the services solution that we created earlier and name it BoundService
.
Let's have a look at steps required to implement a bound service:
BoundService
project and name it MyBoundService
. As this class is the service, we have to add the [Service]
(and subclass Service
) attribute and the OnStartCommand()
method, as seen in the previous recipe. Additionally, MyBoundService
contains a reference to an IBinder
object; override the OnBind()
method, shown as follows:[Service] [IntentFilter(new String[]{"ca.services.MyBoundService"})] public class MyBoundService : Service { IBinder _myBinder = null; public String SayHello() { return "HelloWorld"; } public override StartCommandResult OnStartCommand (Android.Content.Intentintent, StartCommandFlagsflags, intstartId) { Log.Debug ("MyBoundService", "StartCommandResult"); this._myBinder = new MyBoundServiceBinder (this); return StartCommandResult.NotSticky; } public override IBinder OnBind (Intent intent) { Log.Debug ("MyBoundService", "NewClient"); return _myBinder; } }
MyBoundServiceBinder
that extends the Binder
class and owns a reference to MyBoundService
. The constructor of MyBoundServiceBinder
takes MyBoundService
as an argument and populates the private reference with it. Finally, MyBoundServiceBinder
has a getter that returns the MyBoundService
reference:public class MyBoundServiceBinder : Binder { private MyBoundService _myBoundService; public MyBoundService BoundService { get{return _myBoundService;} } public MyBoundServiceBinder (MyBoundService_myBoundService) { this._myBoundService = _myBoundService; Log.Debug ("MyBoundServiceBinder", "NewBinder"); } }
MyBoundServiceBinder
in your main activity the same as an IsBound
Boolean. Also, add a consumerService()
method, shown as follows:[Activity(Label="BoundService",MainLauncher=true)] public class MainActivity : Activity { private MyBoundServiceBinder _myBinder; private Boolean _IsBound; public MyBoundServiceBinder Binder { get{ return_myBinder; } set{ _myBinder=value; } } public Boolean IsBound { get{ return_IsBound; } set{ _IsBound=value; } } public void ConsumeService() { Log.Debug ("MainActivity",_myBinder.BoundService.SayHello ()); } }
MyBoundServiceConnection
that implements the IServiceConnection
interface and owns a reference to MyBoundService
. The MyBoundServiceConnection
class implements the OnServiceConnected()
and OnServiceDisconnected()
methods belonging to the IServiceConnection
interface and contains a reference to the main activity:publicclassMyBoundServiceConnection:Java.Lang.Object,IServiceConnection { MainActivity _myActivity; public MyBoundServiceConnection (MainActivity activity) { this._myActivity = activity; } public void OnServiceConnected (ComponentName name,IBinder service) { Log.Debug ("MyBoundServiceConnection", "IncomingConnection"); var boundServiceBinder = service asMyBoundServiceBinder; if (boundServiceBinder != null) { _myActivity.Binder = boundServiceBinder; _myActivity.isBinded = true; _myActivity.consumeService (); } } public void OnServiceDisconnected (ComponentName name) { _myActivity.isBinded = false; } }
MainActivity
to the MyBoundService
service using the BindService()
method that takes Intent
as arguments, an implementation of IServiceConnection
, and a Bind
flag:protected override void OnCreate(Bundle bundle) { base.OnCreate (bundle); Intent myBoundServiceIntent = new Intent("ca.services.MyBoundService"); StartService (myBoundServiceIntent); cnx = new MyBoundServiceConnection (this); BindService (myBoundServiceIntent, cnx, Bind.AutoCreate); }
[MyBoundService] StartCommandResult [MyBoundServiceBinder] New Binder [MyBoundService] New Client [MyBoundServiceConnection] Incoming Connection [MainActivity] Hello World
In order to understand how it works, we first need to understand the sequence in which the objects interact with each other. The following figure depicts a simplified UML sequence diagram of the collaborations between the objects:
First, in the OnCreate()
method, we make a call to the StartService()
method in the StartService
assembly. This assembly holds code and Xamarin. Android will take care of creating MyBoundService
and triggering a call to the OnStartCommand()
method. Note that there is no message from MyBoundService
to MainActivity
to inform MainActivity
that
MyBoundService
has started correctly. Indeed, the StartService()
method triggers the start and doesn't wait for an acknowledgment from MyBoundService
. Then, MainActivity
obtains a new MyBoundServiceConnection
and asks the BindingAssembly
to bind the MyBoundService
service. The BindingAssembly
will trigger the OnBind()
method of MyBoundService
and the OnBindService()
method of MyBoundServiceConnection
when the binding gets completed. Finally, MyBoundServiceConnection
will invoke the ConsumeService()
method, and the MainActivity
class will consume MyBoundService.SayHello()
method.
You may wonder why we don't call MyBoundService.SayHello()
method directly after BindService
and remove the ConsumeService()
method? The response is simple, we don't know how long it will take for the service to bound effectively, and MyBoundServiceBinder
may be null
if we don't wait long enough. We can have an infinite while(!IsBound){}
in order to wait for the Boolean to be true
. However, the current solution avoids a potential infinite loop and stays simple by avoiding the use of Thread wait and join.
To summarize, clients use MyBoundServiceBinder
to obtain a reference of MyBoundService
, and call a public method on the service. MyBoundServiceBinder
gets populated after the asynchronous call to the BindingAssembly
that triggers the OnBind()
and OnBindService()
methods. Once the OnBindService()
method has been executed, we can be sure that the reference to MyBoundServiceBinder
has been populated, and we can start using the service.
If you want to be sure that your application is running your services, you can simply access the running application on your phone (or emulator) under Settings | Apps | Running, and check how many services are running. In the following picture, we can see one service and one process for the BoundService
application:
18.223.32.230