Panoramica dei servizi associati

Un servizio associato è il server in un'interfaccia client-server. Consente a componenti come attività di associarsi al servizio, inviare richieste, ricevere risposte ed eseguire comunicazione tra processi (IPC). In genere, un servizio associato è attivo solo mentre gestisce un altro componente dell'applicazione e non viene eseguito in background a tempo indeterminato.

Questo documento descrive come creare un servizio associato, incluse le modalità di associazione al servizio da altri componenti dell'applicazione. Per ulteriori informazioni sui servizi in generale, ad esempio su come inviare le notifiche di un servizio e impostare il servizio per l'esecuzione in primo piano, consulta la panoramica dei servizi.

Nozioni di base

Un servizio associato è un'implementazione della classe Service che consente ad altre applicazioni di associarsi e interagire. Per fornire l'associazione di un servizio, devi implementare il metodo di callback onBind(). Questo metodo restituisce un oggetto IBinder che definisce l'interfaccia di programmazione che i client possono utilizzare per interagire con il servizio.

Associazione a un servizio avviato

Come descritto nella panoramica dei servizi, puoi creare un servizio avviato e associato. In altre parole, puoi avviare un servizio chiamando startService(), che consente un'esecuzione illimitata del servizio. Puoi anche consentire a un client di associarsi al servizio chiamando bindService().

Se consenti l'avvio e l'associazione del servizio, all'avvio il sistema non elimina il servizio quando tutti i client si svincolano. Devi invece interrompere esplicitamente il servizio chiamando stopSelf() o stopService().

Anche se di solito implementi onBind() o onStartCommand(), a volte è necessario implementare entrambi. Ad esempio, un music player potrebbe trovare utile consentire l'esecuzione continua del suo servizio e anche fornire associazioni. In questo modo, un'attività può avviare il servizio per riprodurre un po' di musica e la musica continua a essere riprodotta anche se l'utente esce dall'applicazione. Quindi, quando l'utente torna all'applicazione, l'attività può essere associata al servizio per riprendere il controllo della riproduzione.

Per saperne di più sul ciclo di vita del servizio quando aggiungi l'associazione a un servizio avviato, consulta la sezione Gestire il ciclo di vita di un servizio associato.

Un client si associa a un servizio chiamando bindService(). Quando lo fa, deve fornire un'implementazione di ServiceConnection, che monitora la connessione con il servizio. Il valore restituito bindService() indica se il servizio richiesto esiste e se il client è autorizzato ad accedervi.

Quando il sistema Android crea la connessione tra il client e il servizio, chiama onServiceConnected() su ServiceConnection. Il metodo onServiceConnected() include un argomento IBinder, che il client utilizza quindi per comunicare con il servizio associato.

Puoi connettere più client contemporaneamente a un servizio. Tuttavia, il sistema memorizza nella cache il canale di comunicazione del servizio IBinder. In altre parole, il sistema chiama il metodo onBind() del servizio per generare IBinder solo quando il primo client si associa. Il sistema quindi fornisce lo stesso IBinder a tutti i client aggiuntivi associati allo stesso servizio, senza chiamare di nuovo onBind().

Quando l'ultimo client si svincola dal servizio, il sistema elimina il servizio, a meno che il servizio non sia stato avviato utilizzando startService().

La parte più importante dell'implementazione del servizio associato è la definizione dell'interfaccia restituita dal metodo di callback onBind(). La seguente sezione illustra i diversi modi in cui puoi definire l'interfaccia IBinder del tuo servizio.

Crea un servizio associato

Quando crei un servizio che fornisce associazione, devi fornire un IBinder che fornisce l'interfaccia di programmazione che i client possono utilizzare per interagire con il servizio. Esistono tre modi per definire l'interfaccia:

Estendi la classe Binder
Se il servizio è privato per la tua applicazione e viene eseguito nello stesso processo del client, operazione comune, crea l'interfaccia estendendo la classe Binder e restituendo un'istanza da onBind(). Il client riceve Binder e può utilizzarlo per accedere direttamente ai metodi pubblici disponibili nell'implementazione di Binder o in Service.

Questa è la tecnica preferita se il servizio è semplicemente un worker in background per la tua applicazione. L'unico caso d'uso in cui non è il modo migliore per creare la tua interfaccia è se il servizio è utilizzato da altre applicazioni o tra processi separati.

Utilizzare un Messenger
Se vuoi che l'interfaccia funzioni su diversi processi, puoi creare un'interfaccia per il servizio con un Messenger. In questo modo, il servizio definisce un Handler che risponde a diversi tipi di oggetti Message.

Questo Handler è la base per Messenger che può condividere un IBinder con il client, consentendo al client di inviare comandi al servizio utilizzando gli oggetti Message. Inoltre, il client può definire un Messenger personale, in modo che il servizio possa restituire i messaggi.

Questo è il modo più semplice per eseguire la comunicazione tra processi (IPC), perché Messenger accoda tutte le richieste in un unico thread, così non devi progettare il tuo servizio per essere thread-safe.

Usa AIDL
Android Interface Definition Language (AIDL) scompone gli oggetti in primitivi che il sistema operativo è in grado di comprendere e li raggruppa nei vari processi per eseguire l'IPC. La tecnica precedente, utilizzando un Messenger, si basa in realtà su AIDL come struttura sottostante.

Come menzionato nella sezione precedente, Messenger crea una coda di tutte le richieste del client in un singolo thread, in modo che il servizio riceva le richieste una alla volta. Se, tuttavia, vuoi che il tuo servizio gestisca più richieste contemporaneamente, puoi utilizzare direttamente AIDL. In questo caso, il servizio deve essere thread-safe e capacità di multi-threading.

Per utilizzare direttamente AIDL, crea un file .aidl che definisca l'interfaccia di programmazione. Gli strumenti SDK Android utilizzano questo file per generare una classe astratta che implementa l'interfaccia e gestisce l'IPC, che puoi poi estendere all'interno del tuo servizio.

Nota: per la maggior parte delle applicazioni, AIDL non è la scelta migliore per creare un servizio associato, perché potrebbe richiedere funzionalità di multithreading e comportare un'implementazione più complicata. Pertanto, questo documento non illustra come utilizzarlo per il tuo servizio. Se hai la certezza di dover utilizzare direttamente AIDL, consulta il documento AIDL.

Estendi la classe Binder

Se solo l'applicazione locale utilizza il tuo servizio e non deve funzionare su più processi, puoi implementare la tua classe Binder che fornisca al client l'accesso diretto ai metodi pubblici nel servizio.

Nota: funziona solo se il client e il servizio si trovano nella stessa applicazione e nello stesso processo, il che è più comune. Ad esempio, questo metodo funziona bene per un'applicazione musicale che deve associare un'attività al proprio servizio che riproduce musica in background.

Ecco come impostarlo:

  1. Nel servizio, crea un'istanza di Binder che esegua una delle seguenti operazioni:
    • Contiene metodi pubblici che il client può chiamare.
    • Restituisce l'istanza Service corrente, che dispone di metodi pubblici che il client può chiamare.
    • Restituisce un'istanza di un'altra classe ospitata dal servizio con metodi pubblici che il client può chiamare.
  2. Restituisci questa istanza di Binder dal metodo di callback onBind().
  3. Nel client, ricevi Binder dal metodo di callback onServiceConnected() ed effettua chiamate al servizio associato utilizzando i metodi forniti.

Nota: il servizio e il client devono trovarsi nella stessa applicazione in modo che il client possa trasmettere l'oggetto restituito e chiamare correttamente le API. Anche il servizio e il client devono trovarsi nello stesso processo, perché questa tecnica non esegue alcun marshalling tra processi.

Ad esempio, ecco un servizio che fornisce ai client l'accesso ai metodi nel servizio tramite un'implementazione 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 fornisce il metodo getService() per consentire ai client di recuperare l'istanza corrente di LocalService. Ciò consente ai client di chiamare i metodi pubblici nel servizio. Ad esempio, i clienti possono chiamare getRandomNumber() dal servizio.

Ecco un'attività che si associa a LocalService e chiama getRandomNumber() quando un utente fa clic su un pulsante:

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 is something that might hang, then put this request
            // 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 is something that might hang, then put this request
            // 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;
        }
    };
}

L'esempio precedente mostra come il client si associa al servizio utilizzando un'implementazione di ServiceConnection e del callback onServiceConnected(). La sezione successiva fornisce ulteriori informazioni su questa procedura di associazione al servizio.

Nota: nell'esempio precedente, il metodo onStop() svincola il client dal servizio. Svincola i client dai servizi al momento opportuno, come descritto nella sezione Note aggiuntive.

Per ulteriore codice di esempio, consulta la classe LocalService.java e la classe LocalServiceActivities.java in ApiDemos.

Usa un Messenger

Se hai bisogno che il tuo servizio comunichi con processi remoti, puoi utilizzare un Messenger per fornire l'interfaccia per il servizio. Questa tecnica consente di eseguire la comunicazione tra processi (IPC) senza dover utilizzare AIDL.

Utilizzare Messenger per la tua interfaccia è più semplice rispetto all'uso di AIDL perché Messenger mette in coda tutte le chiamate al servizio. Un'interfaccia AIDL pura invia richieste simultanee al servizio, che deve quindi gestire il multithreading.

Per la maggior parte delle applicazioni, il servizio non ha bisogno di eseguire il multi-threading, quindi l'utilizzo di un Messenger consente al servizio di gestire una chiamata alla volta. Se è importante che il servizio sia multithread, utilizza AIDL per definire l'interfaccia.

Ecco un riepilogo di come utilizzare Messenger:

  1. Il servizio implementa un Handler che riceve un callback per ogni chiamata da un client.
  2. Il servizio utilizza Handler per creare un oggetto Messenger (che è un riferimento a Handler).
  3. Messenger crea un valore IBinder che il servizio restituisce ai client da onBind().
  4. I client utilizzano IBinder per creare un'istanza di Messenger (che fa riferimento all'Handler del servizio), che il client utilizza per inviare gli oggetti Message al servizio.
  5. Il servizio riceve ogni Message nel proprio Handler, più precisamente nel metodo handleMessage().

In questo modo, il client non ha metodi per chiamare il servizio. Invece, il client consegna i messaggi (Message oggetti) che il servizio riceve nel suo Handler.

Di seguito è riportato un semplice servizio di esempio che utilizza un'interfaccia 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();
    }
}

Il metodo handleMessage() in Handler è il luogo in cui il servizio riceve i Message in entrata e decide cosa fare in base al membro what.

Tutto ciò che un client deve fare è creare un Messenger basato sul IBinder restituito dal servizio e inviare un messaggio utilizzando send(). Ad esempio, ecco un'attività che si associa al servizio e recapita il messaggio MSG_SAY_HELLO a quest'ultimo:

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

Questo esempio non mostra in che modo il servizio può rispondere al client. Se vuoi che il servizio risponda, devi anche creare un Messenger nel client. Quando il client riceve il callback onServiceConnected(), invia un Message al servizio che include Messenger nel parametro replyTo del metodo send().

Puoi vedere un esempio di come fornire messaggistica bidirezionale negli esempi di MessengerService.java (servizio) e MessengerServiceActivities.java (client).

Associazione a un servizio

I componenti dell'applicazione (client) possono essere associati a un servizio chiamando bindService(). Il sistema Android chiama quindi il metodo onBind() del servizio, che restituisce un valore IBinder per l'interazione con il servizio.

L'associazione è asincrona e bindService() restituisce immediatamente senza restituire IBinder al client. Per ricevere IBinder, il client deve creare un'istanza di ServiceConnection e passarla a bindService(). ServiceConnection include un metodo di callback che il sistema chiama per recapitare l'IBinder.

Nota: solo le attività, i servizi e i fornitori di contenuti possono essere associati a un servizio; non puoi eseguire l'associazione a un servizio da un ricevitore di trasmissione.

Per eseguire l'associazione a un servizio del tuo client, segui questi passaggi:

  1. Implementa ServiceConnection.

    L'implementazione deve sostituire due metodi di callback:

    onServiceConnected()
    Il sistema esegue la chiamata per consegnare il valore IBinder restituito dal metodo onBind() del servizio.
    onServiceDisconnected()
    Il sistema Android chiama questa funzionalità quando la connessione al servizio si interrompe inaspettatamente, ad esempio quando il servizio si arresta in modo anomalo o viene interrotto. Questa azione non viene chiamata quando il client si svincola.
  2. Chiama bindService(), superando l'implementazione ServiceConnection.

    Nota: se il metodo restituisce false, il client non ha una connessione valida al servizio. Tuttavia, chiama unbindService() nel tuo client. In caso contrario, il client impedisce l'arresto del servizio quando è inattivo.

  3. Quando il sistema chiama il tuo metodo di callback onServiceConnected(), puoi iniziare a effettuare chiamate al servizio utilizzando i metodi definiti dall'interfaccia.
  4. Per disconnetterti dal servizio, chiama il numero unbindService().

    Se il client è ancora associato a un servizio quando l'app elimina il client, l'eliminazione provoca la disconnessione del client. È consigliabile svincolare il client non appena ha terminato l'interazione con il servizio. In questo modo il servizio inattivo verrà arrestato. Per ulteriori informazioni sui momenti appropriati per eseguire l'associazione e lo svincolo, consulta la sezione Note aggiuntive.

L'esempio seguente connette il client al servizio creato in precedenza estendendo la classe Binder, quindi non deve fare altro che eseguire il cast dell'elemento IBinder restituito alla classe LocalBinder e richiedere l'istanza 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 questo ServiceConnection, il client può associarsi a un servizio passandolo a bindService(), come mostrato nell'esempio seguente:

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);
  • Il primo parametro di bindService() è un Intent che denomina esplicitamente il servizio da associare.

    Attenzione: se utilizzi un intent per eseguire l'associazione a un Service, assicurati che la tua app sia sicura utilizzando un intent esplicito. L'utilizzo di un intent implicito per avviare un servizio rappresenta un rischio per la sicurezza perché non puoi sapere quale servizio risponda all'intent e l'utente non può vedere quale servizio viene avviato. A partire da Android 5.0 (livello API 21), il sistema genera un'eccezione se chiami bindService() con un intent implicito.

  • Il secondo parametro è l'oggetto ServiceConnection.
  • Il terzo parametro è un flag che indica le opzioni per l'associazione, di solito BIND_AUTO_CREATE, per creare il servizio, se non è già attivo. Altri valori possibili sono BIND_DEBUG_UNBIND, BIND_NOT_FOREGROUND o 0 per nessuno.

Note aggiuntive

Di seguito sono riportate alcune note importanti sull'associazione a un servizio:

  • Blocca sempre le eccezioni DeadObjectException, che vengono generate quando la connessione si interrompe. Questa è l'unica eccezione generata dai metodi remoti.
  • Gli oggetti vengono conteggiati come riferimento nei processi.
  • In genere devi associare e annullare l'associazione durante i momenti di attivazione e rimozione corrispondenti del ciclo di vita del client, come descritto nei seguenti esempi:
    • Se devi interagire con il servizio solo mentre la tua attività è visibile, esegui il binding durante onStart() e svincola durante onStop().
    • Se vuoi che l'attività riceva risposte anche quando è interrotta in background, esegui un collegamento durante onCreate() e svincola durante onDestroy(). Tieni presente che ciò implica che la tua attività deve utilizzare il servizio per tutto il tempo in cui è in esecuzione, anche in background. Di conseguenza, quando il servizio si trova in un altro processo, il peso del processo aumenta ed è più probabile che venga interrotto dal sistema.

    Nota: in genere non devi vincolare e svincolare durante i callback onResume() e onPause() dell'attività, perché questi callback vengono eseguiti a ogni transizione del ciclo di vita. Limita al minimo l'elaborazione che avviene in corrispondenza di queste transizioni.

    Inoltre, se più attività nella tua applicazione si associano allo stesso servizio e si verifica una transizione tra due di queste attività, il servizio potrebbe essere eliminato e ricreato quando l'attività corrente si svincola (durante la pausa) prima dell'associazione di quella successiva (durante il ripristino). Il modo in cui le attività coordinano i loro cicli di vita è descritto in Il ciclo di vita delle attività.

Per ulteriore codice di esempio che mostra come eseguire l'associazione a un servizio, consulta la classe RemoteService.java in ApiDemos.

Gestisci il ciclo di vita di un servizio associato

Quando un servizio non è associato a tutti i client, il sistema Android lo elimina (a meno che non sia stato avviato utilizzando startService()). In questo modo, non devi gestire il ciclo di vita del tuo servizio se è puramente un servizio associato. Il sistema Android lo gestisce per te a seconda che sia vincolato a client.

Tuttavia, se scegli di implementare il metodo di callback onStartCommand(), devi interrompere esplicitamente il servizio poiché ora il servizio viene considerato avviato. In questo caso, il servizio rimane in esecuzione finché non si arresta con stopSelf() o un altro componente chiama stopService(), a prescindere dal fatto che sia associato a un client.

Inoltre, se il servizio è stato avviato e accetta l'associazione, quando il sistema chiama il tuo metodo onUnbind(), puoi facoltativamente restituire true se vuoi ricevere una chiamata a onRebind() la prossima volta che un client si associa al servizio. onRebind() restituisce void, ma il client riceve comunque IBinder nel callback onServiceConnected(). La figura seguente illustra la logica di questo tipo di ciclo di vita.

Figura 1. Il ciclo di vita di un servizio avviato e che consente anche l'associazione.

Per saperne di più sul ciclo di vita di un servizio avviato, vedi la panoramica dei servizi.