Sincronizza i dati permanenti

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 per Wear OS per comunicare direttamente con la rete. Utilizza le stesse API che usi per lo sviluppo mobile, ma tieni presenti alcune differenze specifiche di Wear OS.

Sincronizzare i dati utilizzando l'API Data Layer di Wear OS

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

Puoi impostare elementi di dati e asset senza connessione a nessun dispositivo. Vengono sincronizzati quando i dispositivi stabiliscono una connessione di rete. Questi dati sono privati per la tua app e sono accessibili all'app soltanto su altri dispositivi.

  • Viene sincronizzato un DataItem su tutti i dispositivi di una rete Wear OS. Di solito sono di dimensioni ridotte.

  • Usa un Asset per trasferire un oggetto più grande, ad esempio un'immagine. Il sistema tiene traccia degli asset già trasferiti ed esegue automaticamente la deduplicazione.

Ascolta gli eventi nei servizi

Estendi il corso WearableListenerService. Il sistema gestisce il ciclo di vita dell'elemento WearableListenerService di base, associandolo al servizio quando deve inviare elementi di dati o messaggi e slegando il servizio quando non sono necessari interventi.

Ascolta gli eventi nelle attività

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

Trasferire dati

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

Gli asset gestiscono automaticamente la memorizzazione nella cache dei dati per impedire la ritrasmissione e risparmiare larghezza di banda Bluetooth. Un modello comune prevede che un'app portatile scarichi un'immagine, la riduca a dimensioni appropriate per la visualizzazione sul dispositivo indossabile e la trasmetta all'app indossabile come asset. I seguenti esempi dimostrano questo pattern.

Nota: anche se la dimensione degli elementi di dati è teoricamente limitata a 100 kB, in pratica è possibile utilizzare elementi di dati più grandi. Nel caso di elementi di dati più grandi, separa i dati in base a percorsi univoci 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 quando trasferisci risorse di grandi dimensioni.

Trasferire una risorsa

Crea l'asset utilizzando uno dei metodi create...() nella classe Asset. Converti una bitmap in uno stream di byte, quindi 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());
}

Successivamente, collega l'asset a un elemento dati con il metodo putAsset() in DataMap o PutDataRequest. Quindi, inserisci l'elemento di dati nel datastore utilizzando il metodo putDataItem(), come mostrato negli esempi riportati di seguito.

Il seguente esempio 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);

Il seguente esempio 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);

Ricevi asset

Quando viene creato un asset, probabilmente vorrai leggerlo ed estrarlo dall'altro lato della connessione. Ecco un esempio di come implementare il callback per rilevare una modifica all'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 la sezione Progetto di esempio Datalayer su GitHub.