Gérer les événements Data Layer sur Wear

Restez organisé à l'aide des collections Enregistrez et classez les contenus selon vos préférences.

Lorsque vous appelez l'API Data Layer, vous pouvez recevoir l'état de l'appel lorsqu'il est terminé. Vous pouvez également écouter les événements de données résultant des modifications de données apportées par votre application n'importe où sur le réseau Wear OS by Google.

Consultez les ressources associées suivantes :

Attendre l'état des appels Data Layer

Les appels de l'API Data Layer (par exemple un appel utilisant la méthode putDataItem de la classe DataClient) renvoient parfois un objet Task<ResultType>. Dès que l'objet Task est créé, l'opération est mise en file d'attente en arrière-plan. Si vous ne faites rien d'autre, l'opération s'achèvera silencieusement. Toutefois, vous aurez généralement besoin d'effectuer une action sur le résultat une fois l'opération terminée. L'objet Task vous permet donc d'attendre l'état du résultat, de manière synchrone ou asynchrone.

Appels asynchrones

Si votre code est exécuté sur le thread UI principal, n'effectuez pas d'appels bloquants à l'API Data Layer. Pour exécuter les appels de manière asynchrone, ajoutez une méthode de rappel à l'objet Task, qui se déclenchera une fois l'opération terminée :

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

Consultez l'API Tasks pour découvrir d'autres options, y compris la possibilité d'enchaîner l'exécution de différentes tâches.

Appels synchrones

Si votre code s'exécute sur un thread de gestionnaire distinct d'un service d'arrière-plan (ce qui est le cas dans un WearableListenerService), les appels peuvent être bloqués sans que cela ne pose problème. Dans ce cas, vous pouvez appeler Tasks.await() au niveau de l'objet Task, qui se bloque jusqu'à la fin de la requête et renvoie un objet Result :

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

Écouter les événements Data Layer

Étant donné que la couche de données synchronise et envoie des données sur l'appareil portable et connecté, il est généralement nécessaire d'écouter les événements importants. Ces événements incluent la création d'éléments de données et la réception de messages.

Pour écouter les événements de la couche de données, deux options s'offrent à vous :

Avec ces deux options, vous remplacez les méthodes de rappel pour les événements de données que vous souhaitez gérer.

Remarque : Concernant l'utilisation de la batterie, un WearableListenerService est enregistré dans le fichier manifeste d'une application et peut la lancer si elle n'est pas déjà en cours d'exécution. Si vous n'avez besoin d'écouter des événements que lorsque votre application est déjà en cours d'exécution (ce qui est souvent le cas avec les applications interactives), n'utilisez pas de WearableListenerService. Enregistrez plutôt un écouteur en direct à l'aide de la méthode addListener correspondant à la classe DataClient, par exemple. Cela peut réduire la charge du système et l'utilisation de la batterie.

Avec un WearableListenerService

Vous créerez généralement des instances de ce service dans vos applications connectées et portables. Si les événements de données ne vous intéressent pas dans l'une de ces applications, vous n'avez pas besoin d'y implémenter ce service.

Par exemple, vous pouvez avoir une application portable qui définit et obtient des objets d'éléments de données, et une application connectée qui écoute ces mises à jour afin d'ajuster son UI en conséquence. L'accessoire connecté ne met jamais à jour aucun des éléments de données. Par conséquent, l'application portable n'écoute aucun événement de données de l'application connectée.

Voici certains des événements que vous pouvez écouter avec WearableListenerService :

  • onDataChanged() : lorsqu'un objet d'élément de données est créé, supprimé ou modifié, le système déclenche ce rappel sur tous les nœuds connectés.
  • onMessageReceived() : un message envoyé par un nœud déclenche ce rappel sur le nœud cible.
  • onCapabilityChanged() : lorsqu'une fonctionnalité annoncée par une instance de votre application devient disponible sur le réseau, cet événement déclenche ce rappel. Si vous recherchez un nœud à proximité, vous pouvez interroger la méthode isNearby() pour les nœuds fournis dans le rappel.

En plus de ceux de cette liste, vous pouvez écouter les événements de ChannelClient.ChannelCallback, comme onChannelOpened().

Tous les événements précédents sont exécutés dans un thread d'arrière-plan, et non sur le thread principal.

Pour créer un WearableListenerService, procédez comme suit :

  1. Créez une classe qui étend WearableListenerService.
  2. Écoutez les événements qui vous intéressent, comme onDataChanged().
  3. Déclarez un filtre d'intent dans le fichier manifeste Android pour informer le système de votre WearableListenerService. Cette déclaration permet au système d'associer votre service si nécessaire.

L'exemple suivant montre comment implémenter un élément WearableListenerService simple :

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 section suivante explique comment utiliser un filtre d'intent avec cet écouteur.

Utiliser des filtres avec WearableListenerService

Un filtre d'intent pour l'exemple WearableListenerService de la section précédente peut se présenter comme suit :

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

Dans ce filtre, l'action DATA_CHANGED remplace l'action BIND_LISTENER précédemment recommandée afin que seuls des événements spécifiques déclenchent ou lancent votre application. Cette modification améliore l'efficacité du système et réduit, entre autres, la consommation de la batterie associée à votre application. Dans cet exemple, la montre écoute l'élément de données /start-activity, et le téléphone écoute la réponse au message /data-item-received.

Les règles standards de correspondance des filtres Android s'appliquent. Vous pouvez spécifier plusieurs services par fichier manifeste, plusieurs filtres d'intent par service, plusieurs actions par filtre et plusieurs strophes de données par filtre. Les filtres peuvent correspondre à un hôte générique ou à un hôte spécifique. Pour établir une correspondance sur un hôte générique, utilisez host="*". Pour établir une correspondance sur un hôte spécifique, spécifiez host=<node_id>.

Vous pouvez également établir une correspondance avec un chemin d'accès littéral ou un préfixe de chemin d'accès. Dans ce cas, vous devez spécifier un hôte générique ou un hôte spécifique. Dans le cas contraire, le chemin d'accès que vous avez spécifié sera ignoré.

Pour en savoir plus sur les types de filtres compatibles avec Wear OS, consultez la documentation de référence de l'API pour WearableListenerService.

Pour en savoir plus sur les filtres de données et les règles de correspondance, consultez la documentation de référence de l'API pour l'élément manifeste data.

Lorsque vous faites correspondre des filtres d'intent, deux règles importantes sont à retenir :

  • Si aucun schéma n'est spécifié pour le filtre d'intent, le système ignore tous les autres attributs d'URI.
  • Si aucun hôte n'est spécifié pour le filtre, le système ignore tous les attributs de chemin.

Avec un écouteur en direct

Si votre application ne s'intéresse qu'aux événements de la couche de données lorsque l'utilisateur interagit avec elle, il se peut qu'elle n'ait pas besoin d'un service de longue durée pour gérer chaque modification des données. Dans ce cas, vous pouvez écouter les événements d'une activité en mettant en œuvre une ou plusieurs des interfaces suivantes :

Pour créer une activité qui écoute des événements de données, procédez comme suit :

  1. Implémentez les interfaces souhaitées.
  2. Dans la méthode onCreate() ou onResume(), appelez Wearable.getDataClient(this).addListener(), MessageClient.addListener(), CapabilityClient.addListener() ou ChannelClient.registerChannelCallback() pour avertir les services Google Play que votre activité souhaite écouter pour les événements de couche de données.
  3. Dans onStop() ou onPause(), annulez l'enregistrement des écouteurs avec DataClient.removeListener(), MessageClient.removeListener(), CapabilityClient.removeListener() ou ChannelClient.unregisterChannelCallback().
  4. Si une activité ne s'intéresse qu'aux événements ayant un préfixe de chemin spécifique, vous pouvez ajouter un écouteur avec un filtre de préfixe approprié afin de ne recevoir que les données pertinentes pour l'état actuel de l'application.
  5. Implémentez onDataChanged(), onMessageReceived(), onCapabilityChanged() ou les méthodes de ChannelClient.ChannelCallback, en fonction des interfaces que vous avez implémentées. Ces méthodes sont appelées sur le thread principal. Vous pouvez également spécifier un Looper personnalisé avec WearableOptions.

Voici un exemple de mise en œuvre de 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());
            }
        }
    }
}

Utiliser des filtres avec des écouteurs en direct

Comme indiqué précédemment sur cette page, tout comme vous pouvez spécifier des filtres d'intent pour les objets WearableListenerService basés sur le fichier manifeste, vous pouvez utiliser des filtres d'intent lorsque vous enregistrez un écouteur via l'API Wearable. Les mêmes règles s'appliquent aux écouteurs en direct basés sur des API et aux écouteurs basés sur le fichier manifeste.

Un modèle courant consiste à enregistrer un écouteur avec un chemin d'accès ou un préfixe de chemin spécifique dans la méthode onResume() d'une activité, puis à le supprimer dans la méthode onPause() de l'activité. Implémenter des écouteurs de cette manière permet à votre application de recevoir des événements de manière plus sélective, améliorant ainsi sa conception et son efficacité.