Panoramica dei servizi associati

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

Questo documento descrive come creare un servizio associato, incluso come associarlo ad altri componenti dell'applicazione. Per ulteriori informazioni sui servizi in generale, ad esempio su come inviare notifiche da un servizio e impostarlo 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 con il servizio. Per fornire l'associazione per un servizio, implementa 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 spiegato nella Panoramica dei servizi, puoi creare un servizio che sia avviato che associato. In altre parole, puoi avviare un servizio chiamando startService(), che consente al servizio di essere eseguito a tempo indeterminato. Puoi anche consentire a un client di eseguire il binding al servizio chiamando bindService().

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

Sebbene di solito si implementi onBind() o onStartCommand(), a volte è necessario implementare entrambi. Ad esempio, un lettore musicale potrebbe trovare utile consentire l'esecuzione del proprio servizio a tempo indeterminato e fornire anche l'associazione. In questo modo, un'attività può avviare il servizio per riprodurre della musica, che continua a essere riprodotta anche se l'utente esce dall'applicazione. In seguito, quando l'utente torna all'applicazione, l'attività può associarsi 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 Gestisci il ciclo di vita di un servizio associato.

Un client si associa a un servizio chiamando bindService(). In questo caso, deve fornire un'implementazione di ServiceConnection, che monitora la connessione con il servizio. Il valore restituito di 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 per comunicare con il servizio associato.

Puoi connettere più client a un servizio contemporaneamente. 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 esegue l'associazione. Il sistema invia quindi lo stesso IBinder a tutti i client aggiuntivi che si associano allo stesso servizio, senza chiamare nuovamente onBind().

Quando l'ultimo client si scollega dal servizio, il sistema lo distrugge, a meno che 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 sezione seguente illustra diversi modi per definire l'interfaccia IBinder del tuo servizio.

Crea un servizio associato

Quando crei un servizio che fornisce l'associazione, devi fornire un IBinder che fornisca 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 tuo servizio è privato per la tua applicazione ed è eseguito nello stesso processo del client, come accade di solito, crea l'interfaccia estendendo la classe Binder e restituendo un'istanza di questa classe 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 quando il tuo servizio è semplicemente un worker in background per la tua applicazione. L'unico caso d'uso in cui questo non è il modo preferito per creare l'interfaccia è se il servizio viene utilizzato da altre applicazioni o in processi separati.

Utilizza un Messenger
Se hai bisogno che la tua interfaccia funzioni su processi diversi, 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 un Messenger che può quindi condividere un IBinder con il client, consentendo al client di inviare comandi al servizio utilizzando oggetti Message. Inoltre, il client può definire un proprio Messenger, in modo che il servizio possa inviare messaggi.

Questo è il modo più semplice per eseguire la comunicazione tra processi (IPC), perché Messenger mette in coda tutte le richieste in un unico thread, in modo che non sia necessario progettare il servizio in modo che sia thread-safe.

Utilizza AIDL
Android Interface Definition Language (AIDL) scompone gli oggetti in primitive che il sistema operativo può comprendere e li raggruppa tra i processi per eseguire l'IPC. La tecnica precedente, che utilizza un Messenger, si basa in realtà su AIDL come struttura sottostante.

Come accennato nella sezione precedente, Messenger crea una coda di tutte le richieste client in un singolo thread, in modo che il servizio riceva le richieste una alla volta. Tuttavia, se vuoi che il tuo servizio gestisca più richieste contemporaneamente, puoi utilizzare direttamente AIDL. In questo caso, il servizio deve essere thread-safe e in grado di supportare il multithreading.

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 può 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 tra i processi, puoi implementare la tua classe Binder che fornisce al client l'accesso diretto ai metodi pubblici nel servizio.

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

Ecco come impostarlo:

  1. Nel tuo 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 eseguire il cast dell'oggetto restituito e chiamare correttamente le sue API. Inoltre, il servizio e il client devono trovarsi nello stesso processo, perché questa tecnica non esegue il marshalling tra i 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. In questo modo i client possono chiamare i metodi pubblici nel servizio. Ad esempio, i client possono chiamare getRandomNumber() dal servizio.

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

Kotlin

class BindingActivity : Activity() {
    private var mService: LocalService? = null
    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) {
            mService = null
            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)
        // Release the reference so the service can be garbage-collected once it's
        // destroyed. onServiceDisconnected() isn't called when the client unbinds.
        mService = null
        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 ?: return
            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);
        // Release the reference so the service can be garbage-collected once it's
        // destroyed. onServiceDisconnected() isn't called when the client unbinds.
        mService = null;
        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) {
            mService = null;
            mBound = false;
        }
    };
}

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

Nota:nell'esempio precedente, il metodo onStop() scollega il client dal servizio e rilascia il riferimento del client al servizio. Scollega i client dai servizi nei momenti appropriati, come descritto nella sezione Note aggiuntive.

Per altri esempi di codice, consulta la classe LocalService.java e la classe LocalServiceActivities.java in ApiDemos.

Utilizza un'app di messaggistica

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

L'utilizzo di un Messenger per l'interfaccia è più semplice rispetto all'utilizzo 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 multi-threading.

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

Ecco un riepilogo di come utilizzare un 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 IBinder che il servizio restituisce ai client da onBind().
  4. I client utilizzano IBinder per creare un'istanza di Messenger (che fa riferimento a Handler del servizio), che il client utilizza per inviare oggetti Message al servizio.
  5. Il servizio riceve ogni Message nel suo Handler, in particolare nel metodo handleMessage().

In questo modo, non esistono metodi che il client può chiamare sul servizio. Il client invia invece messaggi (oggetti Message) che il servizio riceve nel suo Handler.

Ecco un esempio di servizio 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 punto in cui il servizio riceve l'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 invia il messaggio MSG_SAY_HELLO al servizio:

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)
            mService = null
            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);
            mService = null;
            bound = false;
        }
    }
}

Questo esempio non mostra come 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 il Messenger del client nel parametro replyTo del metodo send().

Puoi vedere un esempio di come fornire la messaggistica bidirezionale negli esempi 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 IBinder per interagire 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 inviare IBinder.

Nota:solo attività, servizi e provider di contenuti possono eseguire il binding a un servizio. Non puoi eseguire il binding a un servizio da un broadcast receiver.

Per eseguire il binding a un servizio dal tuo client, segui questi passaggi:

  1. Implementa ServiceConnection.

    La tua implementazione deve eseguire l'override di due metodi di callback:

    onServiceConnected()
    Il sistema chiama questo metodo per inviare l'oggetto IBinder restituito dal metodo onBind() del servizio.
    onServiceDisconnected()
    Il sistema Android chiama questo metodo quando la connessione al servizio viene persa inaspettatamente, ad esempio quando il servizio si arresta in modo anomalo o viene interrotto. Questo metodo non viene chiamato quando il client annulla l'associazione.
  2. Chiama bindService(), passando l'implementazione ServiceConnection.

    Nota: se il metodo restituisce false, il client non dispone di 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 unbindService().

    Se il client è ancora associato a un servizio quando l'app lo distrugge, la distruzione comporta l'annullamento dell'associazione del client. È buona norma scollegare il client non appena ha terminato l'interazione con il servizio. In questo modo, il servizio inattivo viene arrestato. Per saperne di più sui momenti appropriati per l'associazione e l'annullamento dell'associazione, consulta la sezione Note aggiuntive.

L'esempio seguente connette il client al servizio creato in precedenza estendendo la classe Binder, quindi tutto ciò che deve fare è eseguire il cast dell'oggetto IBinder restituito alla classe LocalBinder e richiedere l'istanza LocalService:

Kotlin

var mService: LocalService? = null

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")
        mService = null
        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");
        mService = null;
        mBound = false;
    }
};

Con questo ServiceConnection, il client può eseguire il binding 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 assegna esplicitamente un nome al servizio da associare.

    Attenzione:se utilizzi un intent per eseguire il binding 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 avere la certezza di 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

Ecco alcune note importanti sull'associazione a un servizio:

  • Intercetta sempre le eccezioni DeadObjectException, che vengono generate quando la connessione si interrompe. Questa è l'unica eccezione generata dai metodi remoti.
  • Gli oggetti vengono conteggiati per riferimento tra i processi.
  • In genere, l'associazione e la disassociazione vengono eseguite durante i momenti di attivazione e disattivazione 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 lo sbinding durante onStop().
    • Se vuoi che la tua attività riceva risposte anche quando è interrotta in background, esegui il binding durante onCreate() e l'unbinding 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, quindi quando il servizio è in un altro processo, aumenti il peso del processo ed è più probabile che venga terminato dal sistema.

    Nota: in genere non esegui il binding e l'unbinding durante i callback onResume() e onPause() dell'attività, perché questi callback si verificano a ogni transizione del ciclo di vita. Riduci al minimo l'elaborazione che avviene durante queste transizioni.

    Inoltre, se più attività della 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 disassocia (durante la pausa) prima che l'attività successiva si associ (durante il ripristino). Questa transizione di attività per il modo in cui le attività coordinano i loro cicli di vita è descritta in Il ciclo di vita dell'attività.

Per altri esempi di codice campione che mostrano 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 viene scollegato da tutti i client, il sistema Android lo distrugge (a meno che non sia stato avviato utilizzando startService()). Pertanto, non devi gestire il ciclo di vita del tuo servizio se si tratta puramente di un servizio associato. Il sistema Android lo gestisce per te in base al fatto che sia associato o meno a un client.

Tuttavia, se scegli di implementare il metodo di callback onStartCommand(), devi arrestare esplicitamente il servizio, perché ora è considerato avviato. In questo caso, il servizio viene eseguito finché non si arresta con stopSelf() o un altro componente chiama stopService(), indipendentemente dal fatto che sia associato o meno a un client.

Inoltre, se il servizio viene 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 suo callback onServiceConnected(). La figura seguente illustra la logica di questo tipo di ciclo di vita.

Ciclo di vita di un servizio avviato e associato: vengono chiamati onStartCommand() e onBind(); quando tutti i client vengono dissociati, viene chiamato onUnbind(); alla successiva associazione, viene chiamato onRebind() se onUnbind() ha restituito true, altrimenti onBind(); dopo l'arresto del servizio, viene chiamato onDestroy().
Figura 1. Il ciclo di vita di un servizio avviato che consente anche l'associazione.

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