Sincronizza dati

Questo documento descrive come sincronizzare i dati tra un dispositivo Wear OS e un dispositivo portatile.

Invia e sincronizza i dati direttamente dalla rete

Crea app Wear OS per comunicare direttamente con la rete. Utilizza le stesse API che usi per lo sviluppo mobile, ma tieni presente alcune differenze specifiche di Wear OS.

Sincronizzare i dati utilizzando l'API Wear OS Data Layer

Un DataClient espone un'API per consentire ai componenti di leggere o scrivere in un DataItem o Asset.

È possibile impostare elementi di dati e asset senza essere connessi a dispositivi. Vengono sincronizzati quando i dispositivi stabiliscono una connessione di rete. Questi dati sono privati per la tua app e sono accessibili solo alla tua app su altri dispositivi.

  • Un DataItem viene sincronizzato su tutti i dispositivi di una rete Wear OS. In genere, sono di piccole dimensioni.

  • Utilizza un Asset per trasferire un oggetto più grande, ad esempio un'immagine. Il sistema tiene traccia delle risorse già trasferite ed esegue la deduplicazione automaticamente.

Ascolta gli eventi nei servizi

Estendi la lezione WearableListenerService. Il sistema gestisce il ciclo di vita dell'WearableListenerService di base, eseguendo il binding al servizio quando deve inviare elementi di dati o messaggi e annullando il binding del servizio quando non è necessario alcun lavoro.

Ascolta gli eventi nelle attività

Implementa l'interfaccia OnDataChangedListener. Utilizza questa interfaccia anziché un WearableListenerService quando vuoi rilevare le modifiche solo quando l'utente utilizza attivamente la tua app.

Trasferisci dati

Per inviare oggetti binari di grandi dimensioni tramite il trasporto Bluetooth, ad esempio una registrazione vocale da un altro dispositivo, puoi allegare un Asset a un elemento di dati e poi inserire l'elemento di dati nel datastore replicato.

Gli asset gestiscono automaticamente la memorizzazione nella cache dei dati per evitare la ritrasmissione e per risparmiare larghezza di banda Bluetooth. Un pattern comune prevede che un'app mobile scarichi un'immagine, la ridimensioni a una dimensione appropriata per la visualizzazione sul wearable e la trasmetta all'app wearable come asset. I seguenti esempi mostrano questo pattern.

Nota:anche se le dimensioni degli elementi di dati sono teoricamente limitate a 100 KB, in pratica è possibile utilizzare elementi di dati più grandi. Per gli elementi di dati più grandi, separa i dati in base a percorsi unici ed evita di utilizzare un unico percorso per tutti i dati. Il trasferimento di asset di grandi dimensioni influisce sull'esperienza utente in molti casi, quindi testa le tue app per assicurarti che funzionino correttamente durante il trasferimento di asset di grandi dimensioni.

Trasferire un asset

Crea l'asset utilizzando uno dei metodi create...() nella classe Asset. Converti una bitmap in un flusso di byte e poi chiama createFromBytes() per creare l'asset, come mostrato nell'esempio seguente.

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

Dopodiché, collega l'asset a un elemento di dati con il metodo putAsset() in DataMap o PutDataRequest. Poi inserisci l'elemento di dati nel datastore utilizzando il metodo putDataItem(), come mostrato negli esempi seguenti.

L'esempio seguente utilizza 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'esempio seguente utilizza 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);

Ricevere asset

Quando viene creato un asset, probabilmente vuoi leggerlo ed estrarlo dall'altro lato della connessione. Ecco un esempio di come implementare il callback per rilevare una modifica dell'asset ed estrarlo:

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

Per maggiori informazioni, consulta il progetto di esempio DataLayer su GitHub.