Follow the official documents to quickly go through the bindService overview

😄 Official document of binding service

Binding service overview

A binding service is a server in a client server interface. By binding services, components (such as activities) can bind to services, send requests, receive responses, and perform inter process communication (IPC). Binding services are usually only active when providing services for other application components, and will not run in the background indefinitely.

This article describes how to create binding services, including how to bind to services from other application components.

Basic knowledge

Binding Service is the implementation of Service class, which allows other applications to bind and interact with it. To provide a binding for a Service, you must implement the onBind() callback method. This method returns an IBinder object that defines a programming interface that can be used by the client to interact with the Service.

Bind to started service

As described in the service documentation, you can create a service with both started and bound states. In other words, you can call startService() to start the service and let the service run indefinitely, or you can call bindService() to bind the client to the service.

If you do allow the service to have both started and bound states, after the service is started, the system will not destroy the service after all clients are unbound from the service, but 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 methods at the same time. For example, a music player might find it useful to have its services 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.

For more information on the service life cycle when adding a binding to a started service, see the managing the life cycle of a bound service section.

The client binds to the service by calling bindService(). When invoked, it must provide an implementation of ServiceConnection, which monitors the connection to the service. The return value of bindService() indicates whether the requested service exists and whether clients are allowed to access the service. When the Android system creates a connection between the client and the service, it will call onServiceConnected() on ServiceConnection. The onserviceconnected () method contains an IBinder parameter that the client then uses to communicate with the bound service.

You can connect multiple clients to a service at the same time. However, the system caches the IBinder service communication channel. In other words, only when the first client binds the service, the system will call the onbind () method of the service to generate the IBinder. The IBinder is then passed to all other clients bound to the same service without calling onBind() again.

When the last client unbinds the service, the system destroys the service (unless it is also started through startService()).

In the process of implementing the binding service, the most important step is to define the interface returned by the onBind() callback method. The next section will introduce you to several different ways to define the IBinder interface of the service.

Create binding service

When creating a service that provides binding, you must provide IBinder to provide a programming interface for the client to interact with the service. You can define interfaces in three ways:

Extend Binder class

If the Service is dedicated to your own application and runs in the same process as the client (common), you should create an interface by extending the Binder class and returning an instance of the class from onBind(). After receiving Binder, the client can use it to directly access the public methods provided in Binder implementation or Service.

If the service is only the background worker of your own application, this method should be preferred. The only way you don't create an interface in this way is when other applications or different processes occupy your service.

Using Messenger

To make the interface work across different processes, you can use Messenger to create an interface for the service. In this way, the service will define a Handler to respond to different types of Message objects. This Handler is the basis of Messenger, which can then share an IBinder with the client so that the client can use the Message object to send commands to the service. In addition, the client can also define its own Messenger so that the service can return messages.

This is the easiest way to perform interprocess communication (IPC), because Messenger creates a queue containing all requests in a single thread, so you don't have to thread safe design the service.

Use AIDL

Android interface definition language (AIDL) will decompose objects into primitives, and the operating system can execute IPC by identifying these primitives and grouping them into processes. The previous way of using messenger actually took AIDL as its underlying structure. As mentioned above, Messenger creates a queue containing all client requests in a single thread so that the service can receive 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 meet the requirements of thread safety and be able to multithread.

To use aidl directly, you must create a to define the programming interface aidl file. The Android SDK tool will use this file to generate abstract classes that implement interfaces and handle IPC. You can then extend this class within the service.

Extend Binder class

If your service is only used by local applications and does not need to work across processes, you can implement your own Binder class to allow clients to directly access public methods in the service through this class.

⚠️ Note: this method is only effective when the client and service are in the same application and process (the most common case). For example, this method is very effective for music applications that need to bind the Activity to their own services that play music in the background.

The following is the setting method:

  1. In your service, create a Binder instance that can do one of the following:
  • Contains public methods that clients can call.
  • Returns the current Service instance, which contains public methods that can be called by the client.
  • Returns an instance of another class hosted by the service, which contains public methods that the client can call.
  1. This Binder instance is returned from the onBind() callback method.

  2. On the client side, the Binder is received from the onServiceConnected() callback method, and the binding service is called using the provided method.

⚠️ 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 services allow clients to access methods in the service through Binder:

public class LocalService extends Service {
    // Binder given to clients
    private final IBinder binder = 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() {
            // Return this instance of LocalService so clients can call public methods
            return LocalService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    /** method for clients */
    public int getRandomNumber() {
      return mGenerator.nextInt(100);
    }
}

LocalBinder provides the client with a getService() method to retrieve the current instance of LocalService. In this way, the client can call public methods in the service. For example, the client can call getRandomNumber() in the service.

When you click the button, the following activities will bind to LocalService and call 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() {
        super.onStart();
        // Bind to LocalService
        Intent intent = new Intent(this, LocalService.class);
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        unbindService(connection);
        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 connection = 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;
        }
    };
}

The above example illustrates how the client binds to the service using the implementation of ServiceConnection and the onServiceConnected() callback. The next section describes the process of binding to a service in more detail.

⚠️ Note: in the above example, the onStop() method unbinds the client from the service. As described in other instructions, the client should unbind the service at an appropriate time.

Using Messenger

If you need the service to communicate with remote processes, you can use Messenger to provide an interface for your service. In this way, you can perform interprocess communication (IPC) without using AIDL.

Using Messenger for the interface is easier than using AIDL because Messenger queues all service calls. The pure AIDL interface will send multiple requests to the service at the same time, so the service must perform multithreading.

For most applications, the service does not need to perform multi-threaded processing, so using Messenger allows the service to process one call at a time. If your service must perform multithreading, use AIDL to define the interface.

The following is a summary of how Messenger is used:

  1. The service implements a Handler that receives a callback for each call from the client.
  2. The service uses the Handler to create a Messenger object (which is a reference to the Handler).
  3. Messenger creates an IBinder and the service returns it to the client through onBind().
  4. The client instantiates Messenger (which refers to the Handler of the service) using IBinder, and then sends the Message object to the service.
  5. The service receives each Message in its Handler (specifically, in the handleMessage() method).

In this way, the client has no method to call the service. Instead, the client delivers the Message (Message object) received by the service in its Handler.

The following simple service example shows how to use 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.
     */
    static class IncomingHandler extends Handler {
        private Context applicationContext;

        IncomingHandler(Context context) {
            applicationContext = context.getApplicationContext();
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    Toast.makeText(applicationContext, "hello!", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    Messenger mMessenger;

    /**
     * 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();
        mMessenger = new Messenger(new IncomingHandler(this));
        return mMessenger.getBinder();
    }
}

Note that the service will receive the incoming Message in the Handler's handleMessage() method and decide the next operation according to the what member.

The client only needs to create Messenger according to the IBinder returned by the service, and then send the message using send(). For example, the following is an example of binding to a service and passing msg to the service_ SAY_ Simple Activity of Hello message:

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 bound;

    /**
     * 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);
            bound = 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;
            bound = false;
        }
    };

    public void sayHello(View v) {
        if (!bound) 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() {
        super.onStart();
        // Bind to the service
        bindService(new Intent(this, MessengerService.class), mConnection,
            Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service
        if (bound) {
            unbindService(mConnection);
            bound = false;
        }
    }
}

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 in the client. When the client receives the onServiceConnected() callback, it will send a Message to the service and add the client's Messenger to the replyTo parameter of its send() method.

Bind to service

The application component (client) can bind to the service by calling bindService(). Then, the Android system will call the onBind() method of the service, which will return the IBinder used to interact with the service.

Binding is an asynchronous operation, and bindservice () can return immediately without returning IBinder to the client. To receive IBinder, the client must create a ServiceConnection instance and pass it to bindService(). ServiceConnection contains a callback method, which is called by the system to pass IBinder.

⚠️ Note: only activities, services and content providers can bind to services. You cannot bind to services from broadcast receivers.

To bind to a service from your client, follow these steps:

  1. Implement ServiceConnection.

Your implementation must replace two callback methods:

  • onServiceConnected()
    The system calls this method to pass the IBinder returned by the onBind() method of the service.
  • onServiceDisconnected()
    When the connection to the service is interrupted unexpectedly, such as when the service crashes or is terminated, the Android system will call this method. When the client unbinds, the system will not call this method.
  1. Call bindService() to pass the ServiceConnection implementation.

⚠️ Note: if this method returns false, there is no valid connection between your client and the service. However, your client should still call unbindService(); Otherwise, your client will prevent the service from shutting down when it is idle.

  1. When the system calls the onServiceConnected() callback method, you can use the method defined by the interface to start calling the service.

  2. To disconnect from the service, call unbindService().
    When the application destroys the client, if the client remains bound to the service, the destruction will cause the client to unbind. It is better to unbind the client immediately after the interaction between the client and the service is completed. This can shut down idle services. For details on the appropriate timing for binding and unbinding, see other instructions.

The following example connects the client to the service created above by extending the Binder class, so it only needs to convert the returned IBinder into the LocalBinder class and request 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;
    }
};

As shown in the following example, the client can bind to the service by passing this ServiceConnection to bindService():

Intent intent = new Intent(this, LocalService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
  • The first parameter of bindService() is an Intent, which is used to explicitly name the service to be bound.

⚠️ Note: if you use intent to bind to a Service, please use explicit intent to ensure the security of the application. Starting services with implicit intent is a security risk because you cannot determine which services will respond to intent and users cannot see which services have been started. Starting from Android 5.0 (API level 21), if bindService() is called with implicit intent, the system will throw an exception.

  • The second parameter is the ServiceConnection object.
  • The third parameter is a tag indicating the binding options. To create a service that is not yet active, this parameter should normally be BIND_AUTO_CREATE. Other possible values are BIND_DEBUG_UNBIND and bind_ NOT_ Foreround, or 0 (indicates no such parameter).

Other instructions

Here are some important notes about binding to services:

  • You should always catch a DeadObjectException, which is thrown when the connection is broken. This is the only exception thrown by the remote method.
  • Object is a reference to a cross process count.
  • As described in the following example, you usually need to pair bind and unbind during the bring up and teardown moments that match the client lifecycle:
  • If you only need to interact with the service when the Activity is visible, you should bind it during onStart() and unbind it during onStop().
  • If you want the Activity to receive responses even when it stops running in the background, you can bind during onCreate() and unbind during onDestroy(). Please note that this means that your Activity needs to use services throughout its running process (even during background running). Therefore, if the service is located in other processes, when you increase the weight of the process, the system will increase the possibility of terminating the process.

⚠️ Note: normally, you should not bind or unbind during onResume() and onPause() of an Activity, because these callbacks occur every time you switch the lifecycle state. You should keep the processing during these transitions to a minimum. In addition, if multiple activities in your application are bound to the same service and conversion occurs between two of them, If the current Activity is unbound first (during pause), and then the next Activity is bound again (during recovery), the system may destroy it and re create the service. For information on how an Activity coordinates the Activity transformation of its life cycle, please refer to the Activity documentation.

Manage the lifecycle of binding services

When the binding between the service and all clients is cancelled, the Android system will destroy the service (unless the service is also started by using the startService() call). Therefore, if your service is a pure binding service, there is no need to manage its life cycle. The Android system will manage it on your behalf according to 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 other components call stopService() (regardless of whether the service is bound to any client).

In addition, if your service has started and accepted binding, when the system calls your onUnbind() method, you can choose to return true if you want to receive the onRebind() call the next time the client binds to the service. onRebind() returns a null value, but the client will still receive the IBinder in its onServiceConnected() callback. The following figure illustrates the logic of this lifecycle.

Keywords: Android

Added by SerpentSword on Thu, 30 Dec 2021 07:59:10 +0200