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 risultanti dalle modifiche 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 operazioni, l'operazione viene completata automaticamente.

Tuttavia, di solito 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:

// 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>) { ... }

Consulta l'API Tasks 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 si blocca fino al completamento della richiesta e restituisce un oggetto Result. Ciò è mostrato nell'esempio seguente.

Nota:non chiamare questa funzione mentre ti trovi nel thread principale.

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

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 gli 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 dell'utilizzo della batteria dell'app quando scegli un'implementazione del listener. Un WearableListenerService è registrato nel manifest dell'app e può avviare l'app se non è già in esecuzione. Se devi solo ascoltare gli eventi quando l'app è già in esecuzione, come spesso accade con le applicazioni interattive, non utilizzare un WearableListenerService. Registra invece un ascoltatore live. Ad esempio, utilizza il metodo addListener della classe DataClient. In questo modo, il carico sul sistema e l'utilizzo della batteria possono essere ridotti.

Utilizzare un WearableListenerService

In genere, crei istanze di WearableListenerService sia nelle app per dispositivi indossabili sia in quelle 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 portatile non ascolta 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 stai cercando un nodo nelle vicinanze, puoi eseguire una query sul metodo isNearby() dei nodi forniti nel callback.

Puoi anche ascoltare gli eventi da 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 WearableListenerService:

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

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 il seguente aspetto:

<service
    android:name=".snippets.datalayer.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>

Il filtro di azione DATA_CHANGED indica al sistema che la tua app è interessata agli eventi del data layer.

In questo esempio, lo smartwatch ascolta l'elemento di dati /start-activity e lo smartphone ascolta la risposta del messaggio /data-item-received (DATA_ITEM_RECEIVED_PATH).

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 con 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 saperne di più 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 del 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 ascolta gli eventi di dati:

  1. Implementa le interfacce richieste.
  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 agli 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à deve ricevere solo eventi con un prefisso del percorso specifico, aggiungi un listener con un filtro del prefisso per ricevere solo i dati pertinenti allo stato attuale dell'applicazione.
  5. Implementa onDataChanged(), onMessageReceived(), onCapabilityChanged() o metodi da 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:

class MainActivity : Activity(), DataClient.OnDataChangedListener {

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

    override fun onPause() {
        super.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)
            }
        }
    }
}

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 live basati su API sia a quelli basati su manifest.

Un pattern comune consiste nel 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 gli eventi in modo più selettivo, migliorandone la progettazione e l'efficienza.