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.
Pour voir un bon exemple d'utilisation de l'API Data Layer, consultez l'application Android DataLayer Sample.
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, il faudra généralement 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 asynchrone ou synchrone.
Appels asynchrones
Si votre code est exécuté sur le thread UI principal, n'effectuez pas d'appels bloquants à l'API Data Layer et utilisez une coroutine pour appeler putDataItem :
private suspend fun Context.sendDataAsync(count: Int) { try { val putDataReq: PutDataRequest = PutDataMapRequest.create("/count").run { dataMap.putInt("count_key", count) asPutDataRequest() } val dataItem = Wearable.getDataClient(this).putDataItem(putDataReq).await() handleDataItem(dataItem) } catch (e: Exception) { handleDataItemError(e) } finally { handleTaskComplete() } } private fun handleDataItem(dataItem: DataItem) { } private fun handleDataItemError(exception: Exception) { } private fun handleTaskComplete() { }
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 (comme dans un WearableListenerService), utilisez runBlocking pour effectuer un appel bloquant à putDataItem.
Remarque : N'appelez pas cette méthode sur le thread principal.
private fun Context.sendDataSync(count: Int) = runBlocking { val putDataReq = PutDataMapRequest.create("/count").run { dataMap.putInt("count_key", count) asPutDataRequest() } try { val result = Wearable.getDataClient(this@sendDataSync) .putDataItem(putDataReq) .await() // Logic for success } catch (e: Exception) { // Handle failure } }
É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 tels que les créations d'éléments de données et la réception de messages.
Pour écouter les événements de la couche de données, vous avez deux options :
- Créez un service qui étend
WearableListenerService. - Créez une activité ou une classe qui implémente l'interface
DataClient.OnDataChangedListener.
Avec ces deux options, vous remplacez les méthodes de rappel pour les événements de données que vous souhaitez gérer.
Remarque : Tenez compte de l'utilisation de la batterie par votre application lorsque vous choisissez une implémentation d'écouteur. Un WearableListenerService est enregistré dans le fichier manifeste de l'application et peut lancer l'application 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. Par exemple, utilisez la méthode addListener de la classe DataClient. Cela peut alléger la charge du système et l'utilisation de la batterie.
Utiliser un WearableListenerService
Vous créerez généralement des instances de WearableListenerService dans vos applications pour appareils connectés et portables. Mais si les événements de données ne vous intéressent pas dans l'une de ces applications, il n'est pas indispensable 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 modifications afin d'ajuster son UI en conséquence. L'application connectée 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 à l'aide d'un 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éthodeisNearby()pour les nœuds fournis dans le rappel.
Vous pouvez également écouter des événements de ChannelClient.ChannelCallback, tels que 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 :
- Créez une classe qui étend
WearableListenerService. - Écoutez les événements qui vous intéressent, comme
onDataChanged(). - 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 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 section suivante explique comment utiliser un filtre d'intent avec cet écouteur.
Utiliser des filtres avec un WearableListenerService
Un filtre d'intent pour l'exemple WearableListenerService de la section précédente peut se présenter comme suit :
<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>
Le filtre d'action DATA_CHANGED indique au système que votre application s'intéresse aux événements de couche de données.
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 (DATA_ITEM_RECEIVED_PATH).
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. Pour ce faire, vous devez spécifier un caractère générique ou un hôte spécifique. Dans le cas contraire, le chemin d'accès que vous spécifiez sera ignoré par le système.
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'intents, tenez compte de deux règles importantes :
- 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.
Utiliser 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é.
Pour recommander une approche plus propre et plus sûre, utilisez un Lifecycle Observer. En utilisant un observateur de cycle de vie, vous déplacez la logique d'enregistrement hors de onResume() de l'activité et dans une classe réutilisable distincte qui implémente DefaultLifecycleObserver.
Cette approche permet de garder votre activité simple et d'éviter les bugs courants, comme l'oubli de désenregistrement de l'écouteur.
1. Créer l'écouteur tenant compte du cycle de vie
Cette classe encapsule DataClient.OnDataChangedListener et gère automatiquement son propre abonnement en fonction du cycle de vie de l'activité.
class WearDataLayerObserver( private val dataClient: DataClient, private val onDataReceived: (DataEventBuffer) -> Unit ) : DefaultLifecycleObserver, DataClient.OnDataChangedListener { // Implementation of the DataClient listener override fun onDataChanged(dataEvents: DataEventBuffer) { onDataReceived(dataEvents) } // Automatically register when the Activity starts override fun onResume(owner: LifecycleOwner) { dataClient.addListener(this) } // Automatically unregister when the Activity pauses override fun onPause(owner: LifecycleOwner) { dataClient.removeListener(this) } }
2. Utilisation dans votre activité
Désormais, votre activité n'a plus besoin de remplacer onResume() ni onPause() pour l'API Wear. Vous ajoutez l'observateur une seule fois dans onCreate().
class DataLayerLifecycleActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val dataClient = Wearable.getDataClient(this) // Create the observer and link it to the activity's lifecycle val wearObserver = WearDataLayerObserver(dataClient) { dataEvents -> handleDataEvents(dataEvents) } lifecycle.addObserver(wearObserver) } private fun handleDataEvents(dataEvents: DataEventBuffer) { // ... filter and process events ... } }
Pourquoi cette approche est-elle meilleure ?
- Activité de nettoyage : vous supprimez le code récurrent des méthodes du cycle de vie de l'activité.
- Sécurité :
DefaultLifecycleObserverpermet de vérifier que l'écouteur est supprimé même si l'activité est détruite de manière inattendue, ce qui évite les fuites de mémoire. - Réutilisabilité : vous pouvez brancher ce
WearDataLayerObserverdans n'importe quelle activité ou fragment sans réécrire la logique d'enregistrement. - Découplage : la logique de l'écoute est séparée de la logique de ce qu'il faut faire avec les données.
Utiliser des filtres avec des écouteurs en direct
Comme indiqué précédemment, tout comme vous pouvez spécifier des filtres d'intent pour les objets WearableListenerService basés sur le fichier manifeste, vous pouvez en utiliser lorsque vous enregistrez un écouteur via l'API Wearable. Les mêmes règles s'appliquent aux écouteurs en direct basés sur l'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 à l'aide d'un LifecycleObserver. En implémentant des écouteurs de cette manière, votre application peut recevoir des événements de manière plus sélective, ce qui améliore sa conception et son efficacité.