Persistente Daten synchronisieren

In diesem Dokument wird beschrieben, wie Daten zwischen einem Wear OS-Gerät und einem Handheld-Gerät synchronisiert werden.

Daten direkt über das Netzwerk senden und synchronisieren

Erstellen von Wear OS-Apps zur direkten Kommunikation mit dem Netzwerk. Verwende dieselben APIs wie für die mobile Entwicklung, berücksichtige dabei aber einige Wear OS-spezifische Unterschiede.

Daten mit der Wear OS Data Layer API synchronisieren

Ein DataClient stellt eine API für Komponenten zur Verfügung, die in einem DataItem- oder Asset-Objekt lesen oder schreiben können.

Sie können Datenelemente und Assets festlegen, wenn keine Geräte verbunden sind. Sie werden synchronisiert, wenn die Geräte eine Netzwerkverbindung herstellen. Diese Daten sind nur für Ihre App auf anderen Geräten zugänglich.

  • Ein DataItem wird auf allen Geräten in einem Wear OS-Netzwerk synchronisiert. Sie sind in der Regel klein.

  • Verwende ein Asset, um ein größeres Objekt, z. B. ein Bild, zu übertragen. Das System erfasst, welche Assets bereits übertragen wurden, und führt die automatische Deduplizierung aus.

Auf Ereignisse in Diensten warten

Erweitern Sie die Klasse WearableListenerService. Das System verwaltet den Lebenszyklus der Basis-WearableListenerService. Es wird an den Dienst gebunden, wenn Datenelemente oder Nachrichten gesendet werden müssen, und hebt die Bindung des Dienstes auf, wenn keine Arbeit erforderlich ist.

Auf Ereignisse in Aktivitäten warten

Implementieren Sie die OnDataChangedListener-Schnittstelle. Verwenden Sie diese Schnittstelle anstelle eines WearableListenerService, wenn Änderungen nur dann überwacht werden sollen, wenn der Nutzer die Anwendung aktiv verwendet.

Daten übertragen

Wenn Sie binäre große Objekte über den Bluetooth-Transport senden möchten, z. B. eine Sprachaufnahme von einem anderen Gerät, können Sie ein Asset an ein Datenelement anhängen und dieses dann im replizierten Datenspeicher ablegen.

Assets verarbeiten das Caching von Daten automatisch, um eine erneute Übertragung zu verhindern und die Bluetooth-Bandbreite zu sparen. Ein gängiges Muster ist, dass eine Handheld-App ein Bild herunterlädt, es auf eine für die Darstellung auf dem Wearable geeignete Größe verkleinert und als Asset an die Wearable-App überträgt. Die folgenden Beispiele veranschaulichen dieses Muster.

Hinweis: Obwohl die Größe von Datenelementen theoretisch auf 100 KB begrenzt ist, können in der Praxis größere Datenelemente verwendet werden. Bei größeren Datenelementen trennen Sie die Daten durch eindeutige Pfade und verwenden nicht denselben Pfad für alle Daten. Die Übertragung großer Assets wirkt sich in vielen Fällen auf die Nutzerfreundlichkeit aus. Du solltest deine Apps daher testen, um sicherzustellen, dass sie bei der Übertragung großer Assets gut funktionieren.

Asset übertragen

Erstellen Sie das Asset mit einer der create...()-Methoden in der Klasse Asset. Konvertiere eine Bitmap in einen Bytestream und rufe dann createFromBytes() auf, um das Asset zu erstellen, wie im folgenden Beispiel gezeigt.

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

Als Nächstes hängen Sie das Asset mithilfe der Methode putAsset() in DataMap oder PutDataRequest an ein Datenelement an. Legen Sie das Datenelement dann mit der Methode putDataItem() in den Datenspeicher ab, wie in den folgenden Beispielen gezeigt.

Im folgenden Beispiel wird PutDataRequest verwendet:

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

Im folgenden Beispiel wird PutDataMapRequest verwendet:

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

Assets erhalten

Wenn ein Asset erstellt wird, möchten Sie es wahrscheinlich auf der anderen Seite der Verbindung lesen und extrahieren. Hier ein Beispiel für die Implementierung des Callbacks zum Erkennen einer Asset-Änderung und zum Extrahieren des Assets:

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

Weitere Informationen finden Sie im DataLayer-Beispielprojekt auf GitHub.