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'exemple d'application Android DataLayer Sample.
Attendre l'état des appels Data Layer
Les appels de l'API Data Layer (par exemple un appel utilisant la putDataItem méthode de la
DataClient classe) renvoient parfois un Task<ResultType> objet. 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 se termine 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'
DataClient.OnDataChangedListenerinterface.
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 les é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
DataClient classe. Cela peut réduire la charge sur le système et l'utilisation de la batterie.
Utiliser un WearableListenerService
Vous créerez généralement des instances de WearableListenerService dans des applications pour appareils connectés ou portables. Toutefois, si les événements de données ne vous intéressent pas dans l'une des applications, vous n'avez pas besoin d'implémenter le service dans cette application.
Par exemple, vous pouvez avoir une application portable qui définit et obtient des objets d'éléments de données, et une appli pour wearables qui écoute ces modifications afin d'ajuster son UI en conséquence. L'appli pour wearables 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'appli pour wearables.
Voici quelques-uns des événements que vous pouvez écouter à l'aide de 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 laisNearby()méthode pour les nœuds fournis dans le rappel.
Vous pouvez également écouter les é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 la 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 dans une activité.
Pour une approche plus propre et plus sûre, utilisez un observateur de cycle de vie. En utilisant un
observateur de cycle de vie, vous déplacez la logique d'enregistrement de l'activité
LifecycleResumeEvent vers une classe distincte et réutilisable qui
implémente DefaultLifecycleObserver.
Cette approche permet de limiter la taille de votre activité et d'éviter les bugs courants, comme l'oubli de désenregistrer l'écouteur.
1. Créer l'écouteur tenant compte du cycle de vie
Cette classe encapsule le 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é
Votre activité n'a plus besoin d'utiliser LifecycleResumeEvent ni
onPause pour l'API Wear. Vous enregistrez l'observateur une seule fois dans LaunchedEvent (ou 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 préférable ?
- Activité plus propre : vous supprimez le code passe-partout des méthodes de 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 intégrer ce
WearDataLayerObserverdans n'importe quelle activité ou composable sans réécrire la logique d'enregistrement. - Découplage : la logique permettant de déterminer quand écouter est séparée de la logique permettant de déterminer quoi 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 WearableListenerServicebasé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 de collectAsStateWithLifecycle(). En implémentant des écouteurs de cette manière, votre application peut recevoir des événements de manière plus sélective, améliorant ainsi sa conception et son efficacité.