This is the 9th day of my participation in Gwen Challenge
Basic knowledge of
A binding service is a server in a client-server interface. Binding services enables components (such as activities) to bind to services, send requests, receive responses, and even perform interprocess communication (IPC). Binding services are typically active only when serving other application components and do not run in the background indefinitely.
This article shows you how to create bound services, including how to bind services from other application components. However, you should also see the Android Service summary for more information on general services, such as how to use the Service to deliver notifications, how to set up the Service to run in the foreground, and so on.
A binding Service is an implementation of the Service class that lets other applications bind to and interact with it. To provide the service binding, you must implement the onBind() callback method. The IBinder object returned by this method defines the programming interface that the client uses to interact with the service.
Bind to the started service
As described in the Android Service knowledge summary, you can create services that have both started and bound states. That is, you can start the service by calling startService() and let it run indefinitely; In addition, the client can be bound to the service by calling bindService().
If you do allow the service to have both started and bound states, the system “does not” destroy the service after it is started when all clients are unbound. To do this, you must explicitly stop the service by calling stopSelf() or stopService().
Although you should usually implement onBind() or onStartCommand(), sometimes you need to implement both. For example, a music player might find it useful to let its service run indefinitely and provide bindings at the same time. In this way, the Activity can start the service to play music, and the music will not stop even if the user leaves the application. Then, when the user returns to the application, the Activity can bind to the service and regain playback control.
Be sure to read the Managing the Life cycle of a binding service section to learn more about the life cycle of a service when you add a binding to an started service.
The client can bind to the service by calling bindService(). When invoked, it must provide an implementation of ServiceConnection, which monitors the connection to the service. The bindService() method returns no value immediately, but when the Android system creates a connection between the client and the service, onServiceConnected() is called by the Android system to the ServiceConnection, passing the IBinder that the client uses to communicate with the service.
Multiple clients can connect to a service simultaneously. However, the system calls the service’s onBind() method to retrieve the IBinder only on the first client binding. The system can then pass the same IBinder to any other bound client without calling onBind() again.
The system destroys the service when the last client unbinds it (unless startService() also starts it).
When you implement the binding service, the most important step is to define the interface returned by your onBind() callback method. You can define the IBinder interface of a service in several different ways, each of which is described below.
Creating a binding service
When creating a service that provides bindings, you must provide an IBinder that provides a programming interface that clients use to interact with the service. You can define interfaces in three ways:
-
Extending Binder Classes If the service is dedicated to your own application and runs in the same process as the client (common), create the interface by extending the Binder class and returning an instance of it from onBind(). Clients receiving Binder can use it to directly access public methods available in Binder implementations and even services. This approach is preferred if the service is just a background worker thread for your own application. The only reason not to create an interface this way is if your service is occupied by another application or a different process.
-
Using Messenger If you need interfaces to work across different processes, you can use Messenger to create interfaces for services. In this way, services can define handlers that correspond to different types of Message objects. This Handler is the basis for Messenger, which can then share an IBinder with the client so that the client can send commands to the service using the Message object. In addition, the client can define its own Messenger so that the service can send messages back. This is the easiest way to perform interprocess communication (IPC), because Messenger creates queues containing all requests in a single thread, so you don’t have to do thread-safe design for the service.
-
Using AIDL AIDL (Android Interface Definition Language) does all the work of breaking objects into primitives that the operating system can recognize and group into processes to perform IPC. Previous approaches to Messenger actually used AIDL as their underlying structure. As mentioned above, Messenger creates a queue containing all client requests in a single thread so that the service receives one request at a time. However, if you want the service to handle multiple requests at the same time, you can use AIDL directly. In this case, your service must be multithreaded and thread-safe in design. To use AIDL directly, you must create an.aidl file that defines the programming interface. The Android SDK tools use this file to generate an abstract class that implements the interface and handles IPC, which you can then extend within the service.
Note: Most applications “will not” use AIDL to create binding services, as it may require multithreading capability and may lead to increased implementation complexity. Therefore, AIDL is not suitable for most applications, and this article does not explain how to use it for your services. If you are sure you need to use AIDL directly, refer to the AIDL documentation.
In Android system, a process cannot directly access the memory of another process, so it needs to provide some mechanism for communication between different processes. Android officially launched AIDL(Android Interface Definition Language), It is based on Binder. As for the official adoption of Binder, check out why Android has adopted Binder as an IPC mechanism. If the component and service are communicating in the same process, use the first approach; If you are communicating across processes, use the second and third options, the difference being that Messenger cannot handle multi-threaded concurrent requests.
Extend the Binder class
If your service is for local use only and does not require cross-process work, you can implement your own Binder classes that give your clients direct access to public methods in your service.
Note: This method only works in the most common case where the client and service are in the same application and process. For example, this approach works well for music applications that need to bind their activities to their own services that play music in the background.
The following is the specific setting method:
-
In your service, create a Binder instance that meets any of the following requirements:
- Contains public methods that the client can call
- Returns the current
Service
Instance that contains public methods that can be invoked by clients - Or return instances of other classes hosted by the service that contain public methods that can be invoked by the client
-
Return this Binder instance from the onBind() callback method.
-
On the client side, the Binder is received from the onServiceConnected() callback method and the provided method is used to invoke the binding service.
Note: The service and client must be in the same application so that the client can convert the returned object and call its API correctly. The service and client must also be in the same process, because this method does not perform any cross-process marshalling.
For example, the following service enables clients to access methods in the service through Binder implementations:
public class LocalService extends Service {
// Binder given to clients
private final IBinder mBinder = new LocalBinder();
// Random number generator
private final Random mGenerator = new Random();
/** * Class used for the client Binder. Because we know this service always * runs in the same process as its clients, we don't need to deal with IPC. */
public class LocalBinder extends Binder {
LocalService getService(a) {
// Return this instance of LocalService so clients can call public methods
return LocalService.this; }}@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
/** method for clients */
public int getRandomNumber(a) {
return mGenerator.nextInt(100); }}Copy the code
LocalBinder provides clients with a getService() method to retrieve the current instance of LocalService. In this way, the client can invoke public methods in the service. For example, a client can call getRandomNumber() in a service.
When the button is clicked, the following Activity binds to LocalService and calls getRandomNumber() :
public class BindingActivity extends Activity {
LocalService mService;
boolean mBound = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
protected void onStart(a) {
super.onStart();
// Bind to LocalService
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop(a) {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false; }}/** Called when a button is clicked (the button in the layout file attaches to * this method with the android:onClick attribute) */
public void onButtonClick(View v) {
if (mBound) {
// Call a method from the LocalService.
// However, if this call were something that might hang, then this request should
// occur in a separate thread to avoid slowing down the activity performance.
int num = mService.getRandomNumber();
Toast.makeText(this."number: "+ num, Toast.LENGTH_SHORT).show(); }}/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false; }}; }Copy the code
The example above shows how a client can bind to a service using its implementation of ServiceConnection and onServiceConnected() callback. The process of binding to a service is described in more detail below.
Note: In the above example, the onStop() method unbinds the client from the service. The client should unbind the service when appropriate, as described in the additional instructions.
Use the Messenger
Compared with AIDL
When you need to perform IPC, using Messenger for your interface is simpler than implementing it using AIDL, because Messenger queues all service calls, whereas a pure AIDL interface sends multiple requests to the service at the same time, and the service then has to deal with multi-threading.
For most applications, the service does not need to perform multithreading, so using Messenger lets the service handle one call at a time. If your service must perform multithreading, you should use AIDL to define the interface.
If you want your service to communicate with remote processes, you can use Messenger to provide an interface for your service. With this approach, you can perform interprocess communication (IPC) without using AIDL.
Here’s a summary of how to use Messenger:
- The service implements a
Handler
, which receives a callback from each call from the client Handler
Used to createMessenger
Object (forHandler
A reference)Messenger
To create aIBinder
, the service passesonBind()
Make it return to the client- Client use
IBinder
将Messenger
Reference serviceHandler
), and then use the latter willMessage
Object to the service - Services in its
Handler
To be specific, inhandleMessage()
Method) to receive eachMessage
.
In this way, the client does not have a “method” to invoke the service. The “messages” (Message objects) passed by the client are received by the service in its Handler. Here is a simple example of a service that uses the Messenger interface:
public class MessengerService extends Service {
/** Command to the service to display a message */
static final int MSG_SAY_HELLO = 1;
/** * Handler of incoming messages from clients. */
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg); }}}/** * Target we publish for clients to send messages to IncomingHandler. */
final Messenger mMessenger = new Messenger(new IncomingHandler());
/** * When binding to the service, we return an interface to our messenger * for sending messages to the service. */
@Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
returnmMessenger.getBinder(); }}Copy the code
Notice that the service simply receives the incoming Message in the Handler#handleMessage() method and decides what to do next based on the WHAT member.
The client simply creates a Messenger based on the IBinder returned by the service and sends a message using Send (). For example, here is a simple Activity that binds to a service and passes an MSG_SAY_HELLO message to the service:
public class ActivityMessenger extends Activity {
/** Messenger for communicating with the service. */
Messenger mService = null;
/** Flag indicating whether we have called bind on the service. */
boolean mBound;
/** * Class for interacting with the main interface of the service. */
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the object we can use to
// interact with the service. We are communicating with the
// service using a Messenger, so here we get a client-side
// representation of that from the raw IBinder object.
mService = new Messenger(service);
mBound = true;
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
mBound = false; }};public void sayHello(View v) {
if(! mBound)return;
// Create and send a message to the service, using a supported 'what' value
Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0.0);
try {
mService.send(msg);
} catch(RemoteException e) { e.printStackTrace(); }}@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
protected void onStart(a) {
super.onStart();
// Bind to the service
bindService(new Intent(this, MessengerService.class), mConnection,
Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop(a) {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false; }}}Copy the code
Note that this example does not show how the service responds to the client. If you want the service to respond, you also need to create a Messenger on the client side. Then, when the client receives an onServiceConnected() callback, it sends a Message to the service and includes the client’s Messenger in the replyTo parameter of its send() method.
Use AIDL
AIDL stands for Android Interface Definition Language. It is an IDL language that can be used to generate code for IPC. In my opinion, it is a template. Why do you say that? In our case, what actually matters is not the AIDL code we wrote, but the code from an IInterface instance generated by the system. If you generate a few more of these instances and compare them, you’ll see that they all follow a pattern — the same flow, the same structure, with slight variations depending on the specific AIDL file. So AIDL is a template for avoiding the need to write the same code over and over again.
The use of Messenger is also based on AIDL, where Messenger puts all requests in one thread and Service processes them one at a time. However for the AIDL implementation. A service can process multiple requests at the same time (it should process concurrently). In this case, your service must have multithreading capability and be written in a thread-safe manner. To use AIDL directly, you must create an.aidl file that defines the programming interface. The Android SDK tools use this file to automatically generate an abstract class that implements the interface and handling of IPC, which we can then inherit in our own services.
So how do you use AIDL to communicate between threads via bindService()? There are basically the following steps:
The server creates an AIDL file in which the interfaces exposed to the client are declared to implement these interfaces in the service. The client binds to the server. And turn the onServiceConnected() IBinder into an aiDL-generated IInterface instance that calls its exposed methods. Basically the kind of person who doesn’t know what to do with it — it would be a long story if I had to talk about it. With this in mind, here is a brief introduction to AIDL, its specific syntax, exactly how to implement IPC, refer to the Android process communication Messenger&AIDL usage details.
Bind to a service
The binding is asynchronous. BindService () returns immediately and “does not” cause IBinder to return to the client. To receive IBinder, the client must create an instance of ServiceConnection and pass it to bindService(). ServiceConnection includes a callback method that the system calls to pass the IBinder.
Note: Only activities, services, and content providers can be bound to services – you cannot bind to services from broadcast receivers.
Therefore, to bind from your client to the service, you must: 1. Implement ServiceConnection. Your implementation must override two callback methods: onServiceConnected(), which the system calls to pass the IBinder returned by the service’s onBind() method.
OnServiceDisconnected () The Android system calls this method when the connection to the service is disconnected unexpectedly, for example when the service crashes or is terminated. The system “does not” call this method when the client unbinds.
2. Call bindService() and pass the ServiceConnection implementation. 3. When the system calls your onServiceConnected() callback method, you can use the methods defined by the interface to start invoking the service. 4. To disconnect from the service, call unbindService(). If the application destroys the client while it is still bound to the service, destruction causes the client to unbind. It is better to unbind the client as soon as the client interaction with the service is complete. This can turn off idle services. For more information about binding and the appropriate time to unbind, see additional notes.
For example, the following code snippet connects the client to the service created above by extending the Binder class, so it simply converts the returned IBinder to the LocalService class and requests the LocalService instance:
LocalService mService;
private ServiceConnection mConnection = new ServiceConnection() {
// Called when the connection with the service is established
public void onServiceConnected(ComponentName className, IBinder service) {
// Because we have bound to an explicit
// service that is running in our own process, we can
// cast its IBinder to a concrete class and directly access it.
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
// Called when the connection with the service disconnects unexpectedly
public void onServiceDisconnected(ComponentName className) {
Log.e(TAG, "onServiceDisconnected");
mBound = false; }};Copy the code
The client can bind to the service by passing this ServiceConnection to bindService(). Such as:
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
Copy the code
bindService()
The first argument to is aIntent
, used to explicitly name the service to bind to (although the Intent may be implicit)- The second parameter is
ServiceConnection
object - The third parameter is a flag indicating binding options. It usually should be
BIND_AUTO_CREATE
To create services that have not yet been activated. Other possible values areBIND_DEBUG_UNBIND
和BIND_NOT_FOREGROUND
Or,0
(Indicates none.)
Additional instructions
Note: In general, do not bind and unbind during the Activity’s onResume() and onPause() phases, because these callbacks occur during each lifecycle transition, and you should keep processing that occurs during these transitions to a minimum. In addition, if multiple activities within your application are bound to the same service, and there is a transition between two of them, the system may destroy the service and rebuild it if the current Activity is unbound (during pause) before the next Activity binding (during recovery). (The Activity documentation describes this Activity transformation about how an Activity coordinates its lifecycle.)
Manage the life cycle of the bound service
When all bindings between the service and all clients are unbound, the Android system destroys the service (unless it is also started with onStartCommand()). Therefore, if your service is purely a bound service, there is no need to manage its life cycle – The Android system manages it for you based on whether it is bound to any client.
However, if you choose to implement the onStartCommand() callback method, you must explicitly stop the service because the system now treats the service as started. In this case, the service will run until it stops itself through stopSelf() or until some other component calls stopService(), regardless of whether it is bound to any client.
In addition, if your service is started and binding is accepted, you have the option to return true if you want to receive onRebind() when the system calls your onUnbind() method the next time the client binds to the service. OnRebind () returns a null value, but the client still receives IBinder in its onServiceConnected() callback. The diagram below illustrates the logic of this lifecycle.
Other intellectual
Messenger vs. AIDL
For one thing, Messenger is certainly much easier to implement — at least without writing an AIDL file (though if you dig deep, the underlying implementation is still AIDL). Another significant benefit of using Messenger is that it queues all requests, so you almost never have to worry about multithreading.
But then, is the AIDL IPC useless? Of course not. In that case it would have been obsolete. For one thing, if there is a need for concurrent processing in a project, or if there are a large number of concurrent requests, Messenger doesn’t work — its nature means that it can only handle requests serially. In addition, when using Messenger, we can only communicate through Message, but what if we need to call server-side methods directly across processes? Only AIDL can be used.
Therefore, each of these two IPC methods has its own advantages and disadvantages, and the specific use of which depends on the specific needs – of course, use as simple as possible.
reference
Android Developers/Docs/ Guides/Binding services
Android Services: Binder, Messenger, AIDL (2)