Synchroniser les données persistantes

Ce document explique comment synchroniser des données entre un appareil Wear OS et un appareil portable.

Envoyer et synchroniser des données directement à partir du réseau

Créez des applications Wear OS pour communiquer directement avec le réseau. Utilisez les mêmes API que pour le développement mobile, mais gardez à l'esprit les différences spécifiques à Wear OS.

Synchroniser des données à l'aide de l'API Wear OS Data Layer

Un DataClient expose une API pour que les composants puissent lire ou écrire dans un DataItem ou un Asset.

Il est possible de définir des éléments de données et des éléments sans être connecté à aucun appareil. Ils sont synchronisés lorsque les appareils établissent une connexion réseau. Ces données sont privées et ne sont accessibles par votre application que sur d'autres appareils.

  • Un DataItem est synchronisé sur tous les appareils d'un réseau Wear OS. Ils sont généralement de petite taille.

  • Utilisez un Asset pour transférer un objet plus volumineux, comme une image. Le système garde la trace des éléments qui ont déjà été transférés et effectue une déduplication automatique.

Écouter les événements dans les services

Étendez la classe WearableListenerService. Le système gère le cycle de vie du WearableListenerService de base en l'associant au service lorsqu'il doit envoyer des éléments de données ou des messages et en le dissociant lorsqu'aucune tâche n'est nécessaire.

Écouter les événements dans les activités

Implémentez l'interface OnDataChangedListener. Utilisez cette interface à la place d'un WearableListenerService lorsque vous souhaitez écouter les modifications uniquement lorsque l'utilisateur utilise activement votre application.

Transférer des données

Pour envoyer des objets binaires volumineux via le transport Bluetooth, tels qu'un enregistrement vocal à partir d'un autre appareil, vous pouvez associer un Asset à un élément de données, puis placer l'élément de données dans le datastore répliqué.

Les éléments gèrent automatiquement la mise en cache des données pour empêcher la retransmission et préserver la bande passante Bluetooth. Il est courant de télécharger une image, de la réduire à une taille appropriée pour l'afficher sur l'objet connecté et de la transmettre à l'application connectée en tant qu'élément. Les exemples suivants illustrent ce schéma.

Remarque : Bien que la taille des éléments de données soit théoriquement limitée à 100 Ko, en pratique, vous pouvez utiliser des éléments de données plus volumineux. Pour les éléments de données plus volumineux, séparez les données par des chemins uniques et éviter d'utiliser un seul chemin pour toutes les données. Dans de nombreux cas, le transfert d'éléments volumineux affecte l'expérience utilisateur. Par conséquent, testez vos applications pour vous assurer qu'elles fonctionnent bien lors de ce type de transfert.

Transférer un élément

Créez l'élément à l'aide de l'une des méthodes create...() de la classe Asset. Convertissez un bitmap en flux d'octets, puis appelez createFromBytes() pour créer l'élément, comme illustré dans l'exemple suivant.

Kotlin

private fun createAssetFromBitmap(bitmap: Bitmap): Asset =
    ByteArrayOutputStream().let { byteStream ->
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteStream)
        Asset.createFromBytes(byteStream.toByteArray())
    }

Java

private static Asset createAssetFromBitmap(Bitmap bitmap) {
    final ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteStream);
    return Asset.createFromBytes(byteStream.toByteArray());
}

Associez ensuite cet élément à un élément de données avec la méthode putAsset() dans DataMap ou PutDataRequest. Placez ensuite l'élément de données dans le datastore à l'aide de la méthode putDataItem(), comme indiqué dans les exemples suivants.

L'exemple suivant utilise PutDataRequest:

Kotlin

val asset: Asset = BitmapFactory.decodeResource(resources, R.drawable.image).let { bitmap ->
    createAssetFromBitmap(bitmap)
}
val request: PutDataRequest = PutDataRequest.create("/image").apply {
    putAsset("profileImage", asset)
}
val putTask: Task<DataItem> = Wearable.getDataClient(context).putDataItem(request)

Java

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image);
Asset asset = createAssetFromBitmap(bitmap);
PutDataRequest request = PutDataRequest.create("/image");
request.putAsset("profileImage", asset);
Task<DataItem> putTask = Wearable.getDataClient(context).putDataItem(request);

L'exemple suivant utilise PutDataMapRequest:

Kotlin

val asset: Asset = BitmapFactory.decodeResource(resources, R.drawable.image).let { bitmap ->
    createAssetFromBitmap(bitmap)
}
val request: PutDataRequest = PutDataMapRequest.create("/image").run {
    dataMap.putAsset("profileImage", asset)
    asPutDataRequest()
}
val putTask: Task<DataItem> = Wearable.getDataClient(context).putDataItem(request)

Java

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image);
Asset asset = createAssetFromBitmap(bitmap);
PutDataMapRequest dataMap = PutDataMapRequest.create("/image");
dataMap.getDataMap().putAsset("profileImage", asset);
PutDataRequest request = dataMap.asPutDataRequest();
Task<DataItem> putTask = Wearable.getDataClient(context).putDataItem(request);

Recevoir les éléments

Lorsqu'un élément est créé, vous souhaitez probablement le lire et l'extraire de l'autre côté de la connexion. Voici un exemple d'implémentation du rappel pour détecter une modification d'élément et l'extraire :

Kotlin

override fun onDataChanged(dataEvents: DataEventBuffer) {
    dataEvents
            .filter { it.type == DataEvent.TYPE_CHANGED && it.dataItem.uri.path == "/image" }
            .forEach { event ->
                val bitmap: Bitmap? = DataMapItem.fromDataItem(event.dataItem)
                        .dataMap.getAsset("profileImage")
                        .let { asset -> loadBitmapFromAsset(asset) }
                // Do something with the bitmap
            }
}

fun loadBitmapFromAsset(asset: Asset): Bitmap? {
    // Convert asset into a file descriptor and block until it's ready
    val assetInputStream: InputStream? =
            Tasks.await(Wearable.getDataClient(context).getFdForAsset(asset))
            ?.inputStream

    return assetInputStream?.let { inputStream ->
        // Decode the stream into a bitmap
        BitmapFactory.decodeStream(inputStream)
    } ?: run {
        Log.w(TAG, "Requested an unknown Asset.")
        null
    }
}

Java

@Override
public void onDataChanged(DataEventBuffer dataEvents) {
  for (DataEvent event : dataEvents) {
    if (event.getType() == DataEvent.TYPE_CHANGED &&
        event.getDataItem().getUri().getPath().equals("/image")) {
      DataMapItem dataMapItem = DataMapItem.fromDataItem(event.getDataItem());
      Asset profileAsset = dataMapItem.getDataMap().getAsset("profileImage");
      Bitmap bitmap = loadBitmapFromAsset(profileAsset);
      // Do something with the bitmap
    }
  }
}

public Bitmap loadBitmapFromAsset(Asset asset) {
    if (asset == null) {
        throw new IllegalArgumentException("Asset must be non-null");
    }
    // Convert asset into a file descriptor and block until it's ready
    InputStream assetInputStream =
        Tasks.await(Wearable.getDataClient(context).getFdForAsset(asset))
            .getInputStream();
    if (assetInputStream == null) {
        Log.w(TAG, "Requested an unknown Asset.");
        return null;
    }
    // Decode the stream into a bitmap
    return BitmapFactory.decodeStream(assetInputStream);
}

Pour en savoir plus, consultez l'exemple de projet DataLayer sur GitHub.