Descripción general de los servicios enlazados

Un servicio enlazado es el servidor de una interfaz cliente-servidor. Permite que los componentes (como actividades) se enlacen con el servicio, envíen solicitudes, reciban respuestas y establezcan comunicaciones entre procesos (IPC). Un servicio enlazado, por lo general, solo está activo mientras le presta servicio a otro componente de la aplicación y no se ejecuta en segundo plano indefinidamente.

Este documento describe cómo crear un servicio enlazado, además de cómo establecer un enlace con el servicio desde otros componentes de la aplicación. Para obtener información adicional acerca de los servicios en general (por ejemplo, cómo enviar notificaciones desde un servicio, cómo configurar el servicio para que se ejecute en primer plano, etc.), consulta el documento Servicios.

Conceptos básicos

Un servicio enlazado es una implementación de la clase Service que permite que otras aplicaciones se enlacen e interactúen con él. Para enlazar un servicio, debes implementar el método de devolución de llamada onBind(). Este método muestra un objeto IBinder que define la interfaz de programación que los clientes pueden usar para interactuar con el servicio.

Cómo crear un enlace con un servicio iniciado

Como ya se trató en el documento Servicios, puedes crear un servicio que se pueda iniciar y enlazar. Es decir, el servicio se puede iniciar llamando a startService(), lo que permite que el servicio se ejecute indefinidamente, y también puedes permitir que un cliente establezca un enlace con el servicio llamando a bindService().

Si permites que tu servicio se inicie y se enlace, entonces, cuando se haya iniciado el servicio, el sistema no lo destruirá cuando se desenlacen todos los clientes. En su lugar, debes detener explícitamente el servicio llamando a stopSelf() o stopService().

Si bien generalmente implementas onBind() o onStartCommand(), a veces es necesario implementar ambos. Por ejemplo, para un reproductor de música, podría ser útil permitir que su servicio se ejecute indefinidamente y también proporcionar capacidad de creación de enlaces. De esta manera, una actividad puede iniciar el servicio para que reproduzca música y que la música continúe reproduciéndose incluso cuando el usuario salga de la aplicación. Cuando el usuario regrese a la aplicación, la actividad podrá enlazarse con el servicio para volver a tener el control de la reproducción.

Para obtener más información sobre el ciclo de vida del servicio al agregar un enlace a un servicio iniciado, consulta Cómo administrar el ciclo de vida de un servicio enlazado.

Un cliente se enlaza a un servicio llamando a bindService(). Cuando lo hace, debe implementar ServiceConnection, que supervisa la conexión con el servicio. El valor que se muestra de bindService() indica si el servicio solicitado existe y si se le permite al cliente acceder a él. Cuando el sistema Android crea la conexión entre el cliente y el servicio, llama a onServiceConnected() en ServiceConnection. El método onServiceConnected() incluye un argumento IBinder que el cliente utiliza para comunicarse con el servicio enlazado.

Puedes conectar varios clientes a un servicio simultáneamente. Sin embargo, el sistema almacena en caché el canal de comunicación del servicio IBinder. En otras palabras, el sistema llama al método onBind() del servicio para generar el IBinder solo cuando se enlaza el primer cliente. El sistema entonces entrega el mismo IBinder a todos los clientes adicionales que se vinculan a ese mismo servicio, sin volver a llamar a onBind().

Cuando se desenlaza el último cliente del servicio, el sistema destruye el servicio (a menos que también haya sido iniciado por startService()).

La parte más importante de la implementación de tu servicio enlazado es definir la interfaz que mostrará tu método de devolución de llamada onBind(). En la próxima sección, se analizan varias formas diferentes en las que puedes definir la interfaz de tu servicio IBinder.

Cómo crear un servicio enlazado

Al crear un servicio que proporciona la capacidad de crear un enlace, debes proporcionar un IBinder que contenga la interfaz de programación que los clientes pueden usar para interactuar con el servicio. Puedes definir la interfaz de tres maneras:

Extender la clase Binder
Si tu servicio es privado para tu propia aplicación y se ejecuta en el mismo proceso que el cliente (que es lo común), deberías crear tu interfaz extendiendo la clase Binder y mostrando una instancia de ella desde onBind(). El cliente recibe el Binder y puede usarlo para acceder directamente a métodos públicos disponibles en la implementación de Binder o Service.

Esta es la técnica preferida cuando tu servicio es solo un trabajador en segundo plano de tu propia aplicación. El único motivo por el que no deberías crear tu interfaz de esta manera es que otras aplicaciones usen tu servicio o que este se use entre procesos independientes.

Utilizar un objeto Messenger
Si necesitas que tu interfaz funcione en diferentes procesos, puedes crear una para el servicio con un Messenger. De esta manera, el servicio define un Handler que responde a diferentes tipos de objetos Message. Este Handler es la base para un Messenger que luego pueda compartir un IBinder con el cliente y permitir que el cliente envíe comandos al servicio usando objetos Message. Además, el cliente puede definir un Messenger propio de modo que el servicio pueda devolver mensajes.

Esta es la manera más sencilla de establecer comunicación entre procesos (IPC), ya que Messenger pone en cola todas las solicitudes en un solo subproceso para que no debas diseñar tu servicio de modo que sea seguro para subprocesos.

Utilizar AIDL
El Lenguaje de definición de la interfaz de Android (AIDL) realiza todo el trabajo de descomposición de objetos en primitivas que el sistema operativo puede comprender y los ordena entre procesos para establecer IPC. La técnica anterior, que utiliza un Messenger, se basa en AIDL como su estructura subyacente. Como se indicó anteriormente, el Messenger crea una cola de todas las solicitudes del cliente de modo que el servicio reciba las solicitudes de a una. Sin embargo, si quieres que tu servicio maneje varias solicitudes de forma simultánea, puedes usar AIDL directamente. En ese caso, tu servicio debe ser seguro para subprocesos y capaz de administrar varios de ellos.

Para usar el AIDL de forma directa, debes crear un archivo .aidl que defina la interfaz de programación. Las herramientas del SDK de Android usan ese archivo para generar una clase abstracta que implemente la interfaz y manipule la IPC, que luego podrás extender en tu servicio.

Nota: La mayoría de las aplicaciones no deben usar AIDL para crear un servicio enlazado, ya que podría requerir capacidades de ejecución de varios subprocesos y dar lugar a una implementación más complicada. Como tal, AIDL no es conveniente para la mayoría de las aplicaciones, y este documento no aborda cómo usarlo en tu servicio. Si estás seguro de que necesitas usar AIDL de forma directa, lee el documento AIDL.

Extender la clase Binder

Si solo la aplicación local usa tu servicio y este no necesita ejecutarse en diferentes procesos, puedes implementar tu propia clase de Binder que le proporcione al cliente acceso directo a los métodos públicos incluidos en el servicio.

Nota: Esto funciona únicamente si el cliente y el servicio se encuentran en la misma aplicación y el mismo proceso, que es lo más común. Por ejemplo, esto puede funcionar bien para una aplicación de música que necesite enlazar una actividad con su propio servicio que reproduce contenido en segundo plano.

Aquí te mostramos cómo configurarlo:

  1. En tu servicio, crea una instancia de Binder que haga lo siguiente:
    • Contener métodos públicos que el cliente pueda llamar
    • Mostrar la instancia Service actual, que tiene métodos públicos que el cliente puede llamar
    • Mostrar una instancia de otra clase alojada por el servicio con métodos públicos que el cliente pueda llamar
  2. Muestra esta instancia Binder desde el método de devolución de llamada onBind().
  3. En el cliente, recibe Binder del método de devolución de llamada onServiceConnected() y realiza llamadas al servicio enlazado usando los métodos proporcionados.

Nota: El servicio y el cliente deben estar en la misma aplicación para que el cliente pueda convertir el objeto mostrado y llamar a sus API de forma correcta. El servicio y el cliente también deben estar en el mismo proceso, ya que esta técnica no realiza ningún tipo de ordenamiento entre procesos.

Por ejemplo, aquí hay un servicio que les brinda a los clientes acceso a métodos del servicio a través de la implementación de un Binder:

Kotlin

class LocalService : Service() {
    // Binder given to clients
    private val binder = LocalBinder()

    // Random number generator
    private val mGenerator = Random()

    /** method for clients  */
    val randomNumber: Int
        get() = mGenerator.nextInt(100)

    /**
     * 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.
     */
    inner class LocalBinder : Binder() {
        // Return this instance of LocalService so clients can call public methods
        fun getService(): LocalService = this@LocalService
    }

    override fun onBind(intent: Intent): IBinder {
        return binder
    }
}

Java

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 proporciona el método getService() para que los clientes obtengan la instancia actual de LocalService. Esto permite a los clientes llamar a métodos públicos en el servicio. Por ejemplo, los clientes pueden llamar a getRandomNumber() desde el servicio.

A continuación, se proporciona una actividad que se enlaza a LocalService y llama a getRandomNumber() cuando se hace clic en un botón:

Kotlin

class BindingActivity : Activity() {
    private lateinit var mService: LocalService
    private var mBound: Boolean = false

    /** Defines callbacks for service binding, passed to bindService()  */
    private val connection = object : ServiceConnection {

        override fun onServiceConnected(className: ComponentName, service: IBinder) {
            // We've bound to LocalService, cast the IBinder and get LocalService instance
            val binder = service as LocalService.LocalBinder
            mService = binder.getService()
            mBound = true
        }

        override fun onServiceDisconnected(arg0: ComponentName) {
            mBound = false
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)
    }

    override fun onStart() {
        super.onStart()
        // Bind to LocalService
        Intent(this, LocalService::class.java).also { intent ->
            bindService(intent, connection, Context.BIND_AUTO_CREATE)
        }
    }

    override fun 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)  */
    fun onButtonClick(v: View) {
        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.
            val num: Int = mService.randomNumber
            Toast.makeText(this, "number: $num", Toast.LENGTH_SHORT).show()
        }
    }
}

Java

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

En el ejemplo anterior, se muestra la manera en que el cliente establece un enlace con el servicio usando una implementación de ServiceConnection y la devolución de llamada onServiceConnected(). La siguiente sección proporciona más información acerca de este proceso de enlace con el servicio.

Nota: En el ejemplo anterior, el método onStop() desvincula al cliente del servicio. Los clientes se deben desvincular de los servicios en el momento apropiado, como se discute en Notas adicionales.

Para acceder a otros ejemplos de código, consulta las clases LocalService.java y LocalServiceActivities.java en ApiDemos.

Utilizar un objeto Messenger

Si necesitas que tu servicio se comunique con procesos remotos, puedes usar un Messenger a fin de proporcionar la interfaz para tu servicio. Esta técnica te permite establecer comunicación entre procesos (IPC) sin la necesidad de usar AIDL.

El uso de un Messenger para tu interfaz es más sencillo que el uso de AIDL, ya que Messenger pone en cola todas las llamadas al servicio. Una interfaz de AIDL pura envía peticiones simultáneas al servicio, que a su vez debe administrar varios servicios.

Para la mayoría de las aplicaciones, el servicio no necesita ejecutar varios subprocesos; por lo tanto, el uso de un Messenger permite que el servicio maneje una llamada por vez. Si es importante que tu servicio ejecute varios subprocesos, debes usar AIDL para definir tu interfaz.

Aquí te mostramos un resumen de cómo usar un Messenger:

  1. El servicio implementa un Handler que recibe una devolución por cada llamada de un cliente.
  2. El servicio usa el Handler para crear un objeto Messenger (que es una referencia al Handler).
  3. El Messenger crea un IBinder que el servicio muestra a los clientes desde onBind().
  4. Los clientes usan el IBinder para crear instancias del Messenger (que hace referencia al Handler del servicio), que el cliente usa para enviar objetos Message al servicio.
  5. El servicio recibe cada Message en su Handler; específicamente, en el método handleMessage().

De esta manera, no hay métodos que el cliente pueda usar para llamar al servicio. En su lugar, el cliente envía mensajes (objetos Message) que el servicio recibe en su Handler.

Aquí te mostramos un ejemplo sencillo de servicio que usa una interfaz Messenger:

Kotlin

/** Command to the service to display a message  */
private const val MSG_SAY_HELLO = 1

class MessengerService : Service() {

    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    private lateinit var mMessenger: Messenger

    /**
     * Handler of incoming messages from clients.
     */
    internal class IncomingHandler(
            context: Context,
            private val applicationContext: Context = context.applicationContext
    ) : Handler() {
        override fun handleMessage(msg: Message) {
            when (msg.what) {
                MSG_SAY_HELLO ->
                    Toast.makeText(applicationContext, "hello!", Toast.LENGTH_SHORT).show()
                else -> super.handleMessage(msg)
            }
        }
    }

    /**
     * When binding to the service, we return an interface to our messenger
     * for sending messages to the service.
     */
    override fun onBind(intent: Intent): IBinder? {
        Toast.makeText(applicationContext, "binding", Toast.LENGTH_SHORT).show()
        mMessenger = Messenger(IncomingHandler(this))
        return mMessenger.binder
    }
}

Java

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();
    }
}

Observa que el método handleMessage() de Handler es donde el servicio recibe el Message entrante y decide qué hacer en función del miembro de what.

Todo lo que el cliente debe hacer es crear un Messenger en función del IBinder mostrado por el servicio y enviar un mensaje con send(). Por ejemplo, esta es una actividad simple que se enlaza con el servicio y le envía el mensaje MSG_SAY_HELLO:

Kotlin

class ActivityMessenger : Activity() {
    /** Messenger for communicating with the service.  */
    private var mService: Messenger? = null

    /** Flag indicating whether we have called bind on the service.  */
    private var bound: Boolean = false

    /**
     * Class for interacting with the main interface of the service.
     */
    private val mConnection = object : ServiceConnection {

        override fun onServiceConnected(className: ComponentName, service: IBinder) {
            // 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 = Messenger(service)
            bound = true
        }

        override fun onServiceDisconnected(className: ComponentName) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected -- that is, its process crashed.
            mService = null
            bound = false
        }
    }

    fun sayHello(v: View) {
        if (!bound) return
        // Create and send a message to the service, using a supported 'what' value
        val msg: Message = Message.obtain(null, MSG_SAY_HELLO, 0, 0)
        try {
            mService?.send(msg)
        } catch (e: RemoteException) {
            e.printStackTrace()
        }

    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)
    }

    override fun onStart() {
        super.onStart()
        // Bind to the service
        Intent(this, MessengerService::class.java).also { intent ->
            bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
        }
    }

    override fun onStop() {
        super.onStop()
        // Unbind from the service
        if (bound) {
            unbindService(mConnection)
            bound = false
        }
    }
}

Java

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

Ten en cuenta que este ejemplo no muestra cómo el servicio puede responderle al cliente. Si quieres que el servicio responda, necesitarás crear también un Messenger en el cliente. Cuando el cliente reciba la devolución de llamada onServiceConnected(), envía un Message al servicio que incluye el Messenger del cliente en el parámetro replyTo del método send().

Puedes ver la manera de proporcionar un sistema de mensajería bidireccional en los ejemplos MessengerService.java (servicio) y MessengerServiceActivities.java (cliente).

Cómo crear un enlace con un servicio

Los componentes de la aplicación (clientes) pueden establecer un enlace con un servicio llamando a bindService(). Luego, el sistema Android llama al método onBind() del servicio, que muestra un IBinder para interactuar con el servicio.

La vinculación es asíncrona, y bindService() se muestra inmediatamente sin mostrarle el IBinder al cliente. Para recibir el IBinder, el cliente debe crear una instancia de ServiceConnection y pasarla a bindService(). ServiceConnection incluye un método de devolución de llamada al que el sistema llama para enviar el IBinder.

Nota: Solo las actividades, los servicios y los proveedores de contenido pueden establecer enlaces con un servicio; no puedes establecer un enlace con un servicio desde un receptor de emisión.

Para establecer un enlace con un servicio desde tu cliente, debes hacer lo siguiente:

  1. Implementa ServiceConnection.

    Tu implementación debe anular dos métodos de devolución de llamada:

    onServiceConnected()
    El sistema lo llama para enviar el IBinder que muestra el método onBind() del servicio.
    onServiceDisconnected()
    El sistema Android lo llama cuando se pierde de forma inesperada la conexión con el servicio, como cuando el servicio falla o se desactiva. No es llamado cuando se desenlaza el cliente.
  2. Llama a bindService() y pasa la implementación de ServiceConnection.

    Nota: Si el método muestra "false", significa que tu cliente no tiene una conexión válida con el servicio. Sin embargo, tu cliente debe seguir llamando a unbindService(); de lo contrario, evitará que finalice el servicio cuando esté inactivo.

  3. Cuando el sistema llama a tu método de devolución de llamada onServiceConnected(), puedes comenzar a realizar llamadas al servicio usando los métodos definidos por la interfaz.
  4. Para desconectarte del servicio, llama a unbindService().

    Si tu cliente aún está enlazado a un servicio cuando tu app destruye el cliente, la destrucción hace que se desenlace el cliente. La práctica recomendada es desvincular el cliente tan pronto como se acabe la interacción con el servicio. Hacer eso permite que se cierre el servicio inactivo. Si deseas obtener más información sobre momentos apropiados para la vinculación y la desvinculación, consulta Notas adicionales.

Por ejemplo, el siguiente fragmento de código conecta al cliente con el servicio antes creado extendiendo la clase Binder; por lo tanto, lo único que debe hacer es transmitir el IBinder mostrado en la clase LocalService y solicitar la instancia LocalService:

Kotlin

var mService: LocalService

val mConnection = object : ServiceConnection {
    // Called when the connection with the service is established
    override fun onServiceConnected(className: ComponentName, service: IBinder) {
        // 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.
        val binder = service as LocalService.LocalBinder
        mService = binder.getService()
        mBound = true
    }

    // Called when the connection with the service disconnects unexpectedly
    override fun onServiceDisconnected(className: ComponentName) {
        Log.e(TAG, "onServiceDisconnected")
        mBound = false
    }
}

Java

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

Con este ServiceConnection, el cliente puede establecer un enlace con un servicio pasándolo a bindService(), como se muestra en el siguiente ejemplo:

Kotlin

Intent(this, LocalService::class.java).also { intent ->
    bindService(intent, connection, Context.BIND_AUTO_CREATE)
}

Java

Intent intent = new Intent(this, LocalService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
  • El primer parámetro de bindService() es un Intent que menciona explícitamente el servicio para enlazar.

    Advertencia: Si utilizas una intent para crear un enlace con un Service, asegúrate de que tu app sea segura mediante el uso de una intent explícita. El uso de una intent explícita para iniciar un servicio es un riesgo de seguridad porque no puedes garantizar qué servicio responderá a la intent, y el usuario no puede ver qué servicio se inicia. A partir de Android 5.0 (nivel de API 21), el sistema arroja una excepción si llamas a bindService() con una intent implícita.

  • El segundo parámetro es el objeto ServiceConnection.
  • El tercer parámetro es un indicador que contiene opciones para el enlace. Normalmente, debería ser BIND_AUTO_CREATE para crear el servicio si aún no está activo. Otros valores posibles son BIND_DEBUG_UNBIND y BIND_NOT_FOREGROUND, o 0 para ninguno.

Notas adicionales

Aquí te proporcionamos algunas notas importantes acerca de cómo establecer enlaces con un servicio:

  • Siempre debes capturar excepciones DeadObjectException, que se producen cuando se pierde la conexión. Esta es la única excepción que producen los métodos remotos.
  • Los objetos se cuentan como referencia entre procesos.
  • Normalmente, sincronizas el enlace y el desenlace durante los momentos de introducción y anulación de correspondencias del ciclo de vida del cliente, como se describe en los siguientes ejemplos:
    • Si debes interactuar con el servicio solo mientras tu actividad esté visible, debes establecer el enlace durante onStart() y deshacerlo durante onStop().
    • Si quieres que tu actividad reciba respuestas aunque esté detenida en segundo plano, puedes establecer el enlace durante onCreate() y deshacerlo durante onDestroy(). Ten en cuenta que esto implica que tu actividad necesita usar el servicio todo el tiempo que se esté ejecutando (incluso en segundo plano), por lo tanto, si el servicio se encuentra en otro proceso, aumentas el peso del proceso y es muy probable que el sistema lo cierre.

    Nota: Generalmente, no realizas ni deshaces enlaces durante onResume() y onPause() de tu actividad, ya que estas devoluciones de llamada tienen lugar en todas las transiciones del ciclo de vida y debes minimizar al máximo el procesamiento que se produce en esas transiciones. Asimismo, si varias actividades de tu aplicación se enlazan con el mismo servicio y se produce una transición entre dos de esas actividades, es posible que el servicio se destruya y se recree mientras la actividad actual se desenlaza (durante una pausa) y antes de que se enlace la siguiente (durante la reanudación). (Esta transición de una actividad en relación con cómo las actividades coordinan su ciclo de vida se describe en el documento Actividades).

Para acceder a otros ejemplos de código en los que se muestre la manera de establecer un enlace con un servicio, lee la clase RemoteService.java en ApiDemos.

Cómo administrar el ciclo de vida de un servicio enlazado

Cuando se desenlaza un servicio de todos los clientes, el sistema Android lo destruye (a menos que también se haya iniciado con onStartCommand()). De este modo, no necesitas administrar el ciclo de vida de tu servicio si este es puramente un servicio de enlace; el sistema Android lo administra por ti en función de si está enlazado a algún cliente.

No obstante, si decides implementar el método de devolución de llamada onStartCommand(), debes detener el servicio de forma explícita, ya que el servicio ahora se considera iniciado. En este caso, el servicio se ejecuta hasta que se detenga por sí mismo con stopSelf() o cuando otro componente llame a stopService(), independientemente de que esté enlazado o no a algún cliente.

Además, si se inicia tu servicio y este acepta el enlace, entonces, cuando el sistema llame a tu método onUnbind(), puedes mostrar true si deseas recibir una llamada para onRebind() la próxima vez que un cliente establezca un enlace con el servicio. onRebind() muestra "void", pero el cliente de todos modos recibe el IBinder en su devolución de llamada onServiceConnected(). En la figura que aparece a continuación, se ilustra la lógica para este tipo de ciclo de vida.

Figura 1: Ciclo de vida de un servicio que se inicia y también admite enlaces.

Para obtener más información acerca del ciclo de vida de un servicio iniciado, lee el documento Servicios.