Gestire gli eventi del livello dati su Wear

Quando effettui una chiamata all'API Data Layer, puoi ricevere lo stato della chiamata al termine. Puoi anche ascoltare gli eventi di dati derivanti dalle modifiche ai dati apportate dalla tua app ovunque sulla rete Wear OS by Google.

Per un esempio di utilizzo efficace dell'API Data Layer, consulta l'app Android DataLayer Sample.

Attendi lo stato delle chiamate del livello dati

Le chiamate all'API Data Layer, ad esempio una chiamata che utilizza il metodo putDataItem della classe DataClient, a volte restituiscono un oggetto Task<ResultType>. Non appena viene creato l'oggetto Task, l'operazione viene messa in coda in background. Se non esegui altre azioni, l'operazione alla fine viene completata in modo invisibile.

Tuttavia, in genere vuoi fare qualcosa con il risultato dopo il completamento dell'operazione, quindi l'oggetto Task ti consente di attendere lo stato del risultato, in modo asincrono o sincrono.

Chiamate asincrone

Se il codice viene eseguito nel thread principale dell'interfaccia utente, non effettuare chiamate di blocco all'API Data Layer. Esegui le chiamate in modo asincrono aggiungendo un metodo di callback all'oggetto Task, che viene attivato al termine dell'operazione:

Kotlin

// Using Kotlin function references
task.addOnSuccessListener(::handleDataItem)
task.addOnFailureListener(::handleDataItemError)
task.addOnCompleteListener(::handleTaskComplete)
...
fun handleDataItem(dataItem: DataItem) { ... }
fun handleDataItemError(exception: Exception) { ... }
fun handleTaskComplete(task: Task<DataItem>) { ... }

Java

// Using Java 8 Lambdas.
task.addOnSuccessListener(dataItem -> handleDataItem(dataItem));
task.addOnFailureListener(exception -> handleDataItemError(exception));
task.addOnCompleteListener(task -> handleTaskComplete(task));

Consulta l' API Task per altre possibilità, inclusa la concatenazione dell'esecuzione di diverse attività.

Chiamate sincrone

Se il codice viene eseguito su un thread del gestore separato in un servizio in background, ad esempio in un WearableListenerService, è normale che le chiamate vengano bloccate. In questo caso, puoi chiamare Tasks.await() sull'oggetto Task, che blocca fino al completamento della richiesta e restituisce un oggetto Result. Ciò è mostrato nell'esempio seguente.

Nota:assicurati di non chiamare questo metodo nel thread principale.

Kotlin

try {
    Tasks.await(dataItemTask).apply {
        Log.d(TAG, "Data item set: $uri")
    }
}
catch (e: ExecutionException) { ... }
catch (e: InterruptedException) { ... }

Java

try {
    DataItem item = Tasks.await(dataItemTask);
    Log.d(TAG, "Data item set: " + item.getUri());
} catch (ExecutionException | InterruptedException e) {
  ...
}

Ascolta gli eventi del livello dati

Poiché il livello dati sincronizza e invia i dati tra i dispositivi portatili e indossabili, in genere devi ascoltare eventi importanti come la creazione di elementi di dati e la ricezione di messaggi.

Per ascoltare gli eventi del livello dati, hai due opzioni:

Con entrambe queste opzioni, esegui l'override dei metodi di callback degli eventi di dati per gli eventi che ti interessano.

Nota:tieni conto del consumo della batteria della tua app quando scegli un'implementazione del listener. Un WearableListenerService è registrato nel manifest dell'app e può avviarla se non è già in esecuzione. Se devi solo ascoltare gli eventi quando la tua app è già in esecuzione, come spesso accade con le applicazioni interattive, non utilizzare un WearableListenerService. Registra invece un ascoltatore dal vivo. Ad esempio, utilizza il metodo addListener della classe DataClient. In questo modo, il carico sul sistema e il consumo della batteria vengono ridotti.

Utilizzare un WearableListenerService

In genere, crei istanze di WearableListenerService sia nelle app per indossabili sia in quelle per dispositivi portatili. Tuttavia, se non ti interessano gli eventi di dati in una delle app, non devi implementare il servizio in quell'app.

Ad esempio, puoi avere un'app portatile che imposta e recupera gli oggetti degli elementi di dati e un'app indossabile che ascolta questi aggiornamenti per aggiornare la sua UI. L'app per indossabili non aggiorna mai nessuno degli elementi di dati, quindi l'app per dispositivi mobili non è in ascolto di eventi di dati dall'app per indossabili.

Alcuni degli eventi che puoi ascoltare utilizzando WearableListenerService sono i seguenti:

  • onDataChanged(): ogni volta che un oggetto elemento di dati viene creato, eliminato o modificato, il sistema attiva questo callback su tutti i nodi connessi.
  • onMessageReceived(): un messaggio inviato da un nodo attiva questo callback sul nodo di destinazione.
  • onCapabilityChanged(): quando una funzionalità pubblicizzata da un'istanza della tua app diventa disponibile sulla rete, questo evento attiva questo callback. Se cerchi un nodo nelle vicinanze, puoi eseguire query sul metodo isNearby() dei nodi forniti nel callback.

Puoi anche ascoltare gli eventi di ChannelClient.ChannelCallback, ad esempio onChannelOpened().

Tutti gli eventi precedenti vengono eseguiti in un thread in background, non nel thread principale.

Per creare un WearableListenerService:

  1. Crea una classe che estenda WearableListenerService.
  2. Ascolta gli eventi che ti interessano, ad esempio onDataChanged().
  3. Dichiara un filtro per intent nel manifest Android per comunicare al sistema il tuo WearableListenerService. Questa dichiarazione consente al sistema di associare il tuo servizio in base alle necessità.

Il seguente esempio mostra come implementare un semplice WearableListenerService:

Kotlin

private const val TAG = "DataLayerSample"
private const val START_ACTIVITY_PATH = "/start-activity"
private const val DATA_ITEM_RECEIVED_PATH = "/data-item-received"

class DataLayerListenerService : WearableListenerService() {

    override fun onDataChanged(dataEvents: DataEventBuffer) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "onDataChanged: $dataEvents")
        }

        // Loop through the events and send a message
        // to the node that created the data item.
        dataEvents.map { it.dataItem.uri }
                .forEach { uri ->
                    // Get the node ID from the host value of the URI.
                    val nodeId: String = uri.host
                    // Set the data of the message to be the bytes of the URI.
                    val payload: ByteArray = uri.toString().toByteArray()

                    // Send the RPC.
                    Wearable.getMessageClient(this)
                            .sendMessage(nodeId, DATA_ITEM_RECEIVED_PATH, payload)
                }
    }
}

Java

public class DataLayerListenerService extends WearableListenerService {
    private static final String TAG = "DataLayerSample";
    private static final String START_ACTIVITY_PATH = "/start-activity";
    private static final String DATA_ITEM_RECEIVED_PATH = "/data-item-received";

    @Override
    public void onDataChanged(DataEventBuffer dataEvents) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "onDataChanged: " + dataEvents);
        }

        // Loop through the events and send a message
        // to the node that created the data item.
        for (DataEvent event : dataEvents) {
            Uri uri = event.getDataItem().getUri();

            // Get the node ID from the host value of the URI.
            String nodeId = uri.getHost();
            // Set the data of the message to be the bytes of the URI.
            byte[] payload = uri.toString().getBytes();

            // Send the RPC.
            Wearable.getMessageClient(this).sendMessage(
                  nodeId,  DATA_ITEM_RECEIVED_PATH, payload);
        }
    }
}

La sezione seguente spiega come utilizzare un filtro per intent con questo listener.

Utilizzare i filtri con WearableListenerService

Un filtro per intent per l'esempio WearableListenerService mostrato nella sezione precedente potrebbe avere questo aspetto:

<service android:name=".DataLayerListenerService" android:exported="true" tools:ignore="ExportedService" >
  <intent-filter>
      <action android:name="com.google.android.gms.wearable.DATA_CHANGED" />
      <data android:scheme="wear" android:host="*"
               android:path="/start-activity" />
  </intent-filter>
</service>

In questo filtro, l'azione DATA_CHANGED sostituisce l'azione BIND_LISTENER consigliata in precedenza, in modo che solo eventi specifici attivino o avviano l'app. Questa modifica migliora l'efficienza del sistema e riduce il consumo della batteria e altri sovraccarichi associati all'app. In questo esempio, lo smartwatch ascolta l'elemento di dati /start-activity e lo smartphone ascolta la risposta al messaggio /data-item-received.

Vengono applicate le regole standard di corrispondenza dei filtri di Android. Puoi specificare più servizi per manifest, più filtri per intent per servizio, più azioni per filtro e più sezioni di dati per filtro. I filtri possono corrispondere a un host jolly o a uno specifico. Per trovare una corrispondenza con un host jolly, utilizza host="*". Per la corrispondenza su un host specifico, specifica host=<node_id>.

Puoi anche trovare una corrispondenza con un percorso letterale o un prefisso di percorso. Per farlo, devi specificare un carattere jolly o un host specifico. In caso contrario, il sistema ignora il percorso specificato.

Per ulteriori informazioni sui tipi di filtri supportati da Wear OS, consulta la documentazione di riferimento dell'API per WearableListenerService.

Per ulteriori informazioni sui filtri dei dati e sulle regole di corrispondenza, consulta la documentazione di riferimento dell'API per l'elemento manifest <data>.

Quando abbini i filtri per intent, ricorda due regole importanti:

  • Se non viene specificato alcuno schema per il filtro per intent, il sistema ignora tutti gli altri attributi URI.
  • Se non viene specificato alcun host per il filtro, il sistema ignora tutti gli attributi del percorso.

Utilizzare un ascoltatore live

Se la tua app si occupa solo degli eventi livello dati quando l'utente interagisce con l'app, potrebbe non aver bisogno di un servizio a lunga esecuzione per gestire ogni modifica dei dati. In questo caso, puoi ascoltare gli eventi in un'attività implementando una o più delle seguenti interfacce:

Per creare un'attività che ascolti gli eventi di dati:

  1. Implementa le interfacce che preferisci.
  2. Nel metodo onCreate() o onResume(), chiama Wearable.getDataClient(this).addListener(), MessageClient.addListener(), CapabilityClient.addListener() o ChannelClient.registerChannelCallback() per comunicare a Google Play Services che la tua attività è interessata ad ascoltare gli eventi del data layer.
  3. In onStop() o onPause(), annulla la registrazione di tutti i listener con DataClient.removeListener(), MessageClient.removeListener(), CapabilityClient.removeListener() o ChannelClient.unregisterChannelCallback().
  4. Se un'attività è interessata solo agli eventi con un prefisso del percorso specifico, puoi aggiungere un listener con un filtro del prefisso appropriato per ricevere solo i dati pertinenti allo stato attuale dell'applicazione.
  5. Implementa onDataChanged(), onMessageReceived(), onCapabilityChanged() o metodi di ChannelClient.ChannelCallback, a seconda delle interfacce che hai implementato. Questi metodi vengono chiamati sul thread principale oppure puoi specificare un Looper personalizzato utilizzando WearableOptions.

Ecco un esempio che implementa DataClient.OnDataChangedListener:

Kotlin

class MainActivity : Activity(), DataClient.OnDataChangedListener {

    public override fun onResume() {
        Wearable.getDataClient(this).addListener(this)
    }

    override fun onPause() {
        Wearable.getDataClient(this).removeListener(this)
    }

    override fun onDataChanged(dataEvents: DataEventBuffer) {
        dataEvents.forEach { event ->
            if (event.type == DataEvent.TYPE_DELETED) {
                Log.d(TAG, "DataItem deleted: " + event.dataItem.uri)
            } else if (event.type == DataEvent.TYPE_CHANGED) {
                Log.d(TAG, "DataItem changed: " + event.dataItem.uri)
            }
        }
    }
}

Java

public class MainActivity extends Activity implements DataClient.OnDataChangedListener {

    @Override
    public void onResume() {
        Wearable.getDataClient(this).addListener(this);
    }

    @Override
    protected void onPause() {
        Wearable.getDataClient(this).removeListener(this);
    }

    @Override
    public void onDataChanged(DataEventBuffer dataEvents) {
        for (DataEvent event : dataEvents) {
            if (event.getType() == DataEvent.TYPE_DELETED) {
                Log.d(TAG, "DataItem deleted: " + event.getDataItem().getUri());
            } else if (event.getType() == DataEvent.TYPE_CHANGED) {
                Log.d(TAG, "DataItem changed: " + event.getDataItem().getUri());
            }
        }
    }
}

Attenzione: prima di utilizzare l'API Wearable Data Layer, verifica che sia disponibile su un dispositivo, altrimenti si verifica un'eccezione. Utilizza la classe GoogleApiAvailability, come implementato in Horologist.

Utilizzare i filtri con gli ascoltatori in tempo reale

Come accennato in precedenza, così come puoi specificare i filtri per intent per gli oggetti WearableListenerService basati sul manifest, puoi utilizzare i filtri per intent quando registri un listener live tramite l'API Wearable. Le stesse regole si applicano sia agli ascoltatori in tempo reale basati su API sia a quelli basati su manifest.

Un pattern comune è registrare un listener con un percorso o un prefisso del percorso specifico nel metodo onResume() di un'attività e poi rimuovere il listener nel metodo onPause() dell'attività. L'implementazione dei listener in questo modo consente alla tua app di ricevere eventi in modo più selettivo, migliorandone la progettazione e l'efficienza.