Sincronizar dados

Este documento descreve como sincronizar dados entre um dispositivo Wear OS e um dispositivo portátil.

Enviar e sincronizar dados diretamente da rede

Crie apps para Wear OS que se comuniquem diretamente com a rede. Use a mesma APIs usadas no desenvolvimento para dispositivos móveis, mas que precisam ser específicas para o Wear OS as diferenças em mente.

Sincronizar dados usando a API Data Layer do Wear OS

Um DataClient expõe uma API para componentes lerem ou gravarem em um DataItem ou Asset

É possível definir itens de dados e recursos quando não há conexão com nenhum dispositivo. Eles são sincronizados quando os dispositivos estabelecem uma conexão de rede. Esses dados é particular para seu app e só pode ser acessado por ele em outros dispositivos.

  • Um DataItem é sincronizado em todos os dispositivos conectados a uma rede do Wear OS. Em geral, eles são pequenos.

  • Use um Asset para transferir um objeto maior, como uma imagem. O sistema acompanha quais recursos já foram transferidos e realiza a eliminação de duplicação automaticamente.

Detectar eventos em serviços

Estenda a classe WearableListenerService. O sistema gerencia ciclo de vida da WearableListenerService base, vinculada ao serviço quando que precisa enviar itens de dados ou mensagens e desvincular o serviço quando não há trabalho necessários.

Detectar eventos em atividades

Implemente a interface OnDataChangedListener. Use esta interface de um WearableListenerService quando quiser detectar mudanças somente quando o o usuário está usando seu app ativamente.

Transferir dados

Enviar objetos grandes binários por Bluetooth, como uma gravação de voz de outro dispositivo, é possível anexar um Asset em um item de dados e, em seguida, coloca o item no repositório de dados replicado.

Os recursos processam automaticamente o armazenamento de dados em cache para evitar a retransmissão e conservar a largura de banda do Bluetooth. É um padrão comum que um app de dispositivo portátil faça o download de uma imagem, diminua a imagem para um tamanho adequado para exibição no wearable e a envie ao app para wearables como um recurso. Os exemplos abaixo demonstram esse padrão.

Observação: teoricamente, o tamanho dos itens de dados é limitado a 100 KB, mas, na prática, é possível usar itens de dados maiores. Para itens de dados maiores, os desenvolvedores precisam separar os dados por caminhos exclusivos e evitar usar um único caminho para todos os dados. A transferência de recursos grandes afeta a experiência do usuário em muitos casos. Teste seus apps para garantir que eles funcionem bem ao transferir recursos grandes.

Transferir um recurso

Crie o recurso usando um dos métodos create...() na classe Asset. Converta um bitmap em um stream de bytes e depois chame createFromBytes() para criar o recurso, conforme mostrado no exemplo abaixo.

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

Depois, anexe o recurso a um item de dados com o método putAsset() em DataMap ou PutDataRequest. Em seguida, coloque o item de dados no armazenamento de dados usando o putDataItem(), conforme mostrado nos exemplos a seguir.

O exemplo a seguir usa 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);

O exemplo a seguir usa 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);

Receber recursos

Quando um recurso é criado, é provável que você queira fazer a leitura e extração dele no outro lado da conexão. Confira um exemplo de como implementar o callback para detectar uma mudança e extrair o recurso:

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

Para saber mais, consulte o projeto de exemplo DataLayer (link em inglês) no GitHub.