Ao chamar a API Data Layer, você pode receber o status da chamada quando ela for concluída. Também é possível ouvir eventos de dados resultantes de mudanças feitas pelo app em qualquer local da rede do Wear OS by Google.
Para um exemplo de como trabalhar com a API Data Layer de maneira eficaz, confira o app de exemplo Android DataLayer (link em inglês).
Aguardar o status das chamadas de Data Layer
As chamadas para a API Data Layer, por exemplo, usando o método putDataItem
da classe
DataClient
,
podem retornar um
objeto
Task<ResultType>
. Assim que o objeto Task
é criado, a operação é colocada
na fila em segundo plano. Se você não fizer nada depois disso, a operação
será concluída silenciosamente.
No entanto, é mais provável que você faça algo com
o resultado quando a operação for concluída. Por isso, o objeto Task
permite
aguardar o status do resultado de forma assíncrona ou síncrona.
Chamadas assíncronas
Se o código estiver sendo executado na linha de execução de interface principal, não faça chamadas de bloqueio para a
API Data Layer. Execute as chamadas de forma assíncrona adicionando um método de callback
ao objeto Task
, que será acionado quando a operação terminar:
Kotlin
// Using Kotlin function references task.addOnSuccessListener(::handleDataItem) task.addOnFailureListener(::handleDataItemError) task.addOnCompleteListener(::handleTaskComplete) ... fun handleDataItem(dataItem: DataItem) { ... } fun handleDataItemError(exception: Exception) { ... } fun handleTaskComplete(task: Task<DataItem>) { ... }
Java
// Using Java 8 Lambdas. task.addOnSuccessListener(dataItem -> handleDataItem(dataItem)); task.addOnFailureListener(exception -> handleDataItemError(exception)); task.addOnCompleteListener(task -> handleTaskComplete(task));
Consulte a API Task para descobrir outras possibilidades, incluindo a opção de encadear a execução de tarefas diferentes.
Chamadas síncronas
Se o código está sendo executado em outra linha de execução do gerenciador em um serviço em segundo plano,
como no caso de
WearableListenerService
,
não há problema em fazer chamadas de bloqueio. Nesse caso, chame Tasks.await()
no objeto Task
,
que fica bloqueado até que a solicitação seja concluída e retorne um
objeto Result
. Esta chamada é mostrada no exemplo abaixo.
Observação: não faça essa chamada na linha de execução principal.
Kotlin
try { Tasks.await(dataItemTask).apply { Log.d(TAG, "Data item set: $uri") } } catch (e: ExecutionException) { ... } catch (e: InterruptedException) { ... }
Java
try { DataItem item = Tasks.await(dataItemTask); Log.d(TAG, "Data item set: " + item.getUri()); } catch (ExecutionException | InterruptedException e) { ... }
Ouvir eventos da Data Layer
Como a camada de dados sincroniza e envia dados entre o dispositivo portátil e o wearable, geralmente é necessário ouvir eventos importantes, como a criação de itens de dados e o recebimento de mensagens.
Para ouvir eventos da Data Layer, você tem duas opções:
- Criar um serviço que estenda
WearableListenerService
. - Criar uma atividade ou classe que implemente a interface
DataClient.OnDataChangedListener
.
Com ambas as opções, é possível substituir os métodos de callback de evento de dados para os eventos que você pretende gerenciar.
Observação: considere o consumo de bateria do app ao escolher
qual listener será implementado. Um WearableListenerService
é registrado no arquivo de manifesto e pode iniciar o app se ele ainda não estiver
em execução. Se você só precisa ouvir eventos quando o app já está em execução
(o que geralmente é o caso para apps interativos), não use um
WearableListenerService
. Em vez disso, registre um listener em tempo real.
Por exemplo, use o método addListener
da classe
DataClient
. Isso pode reduzir a carga no sistema e, assim, reduzir o uso da bateria.
Usar um WearableListenerService
Geralmente, é possível criar instâncias de
WearableListenerService
tanto no app para dispositivos portáteis
quanto no app para wearables. No entanto, se você não quiser usar eventos de dados em um desses apps,
não é necessário implementar o serviço nele.
Por exemplo, você pode ter um app para dispositivos portáteis que configura e recebe objetos de itens de dados e um app para wearables que ouve essas mudanças para atualizar a interface. Como a versão para wearables não atualiza nenhum item de dados, o app para dispositivos portáteis não ouve eventos de dados dela.
Alguns dos eventos que podem ser ouvidos usando
WearableListenerService
são:
-
onDataChanged()
: sempre que um objeto de item de dados é criado, excluído ou modificado, o sistema aciona esse callback em todos os nós conectados. -
onMessageReceived()
: uma mensagem enviada de um nó aciona esse callback no nó de destino. -
onCapabilityChanged()
: quando um recurso divulgado por uma instância do app fica disponível na rede, o evento aciona esse callback. Caso precise de um nó próximo, você pode consultar o métodoisNearby()
dos nós fornecidos no callback.
Também é possível ouvir eventos do
ChannelClient.ChannelCallback
, como onChannelOpened()
.
Todos os eventos acima são executados em uma linha de execução em segundo plano, e não na principal.
Para criar um WearableListenerService
, siga estas etapas:
- Crie uma classe que estenda o
WearableListenerService
. - Detecte eventos em que você tenha interesse, como
onDataChanged()
. - Declare um filtro de intent no manifesto do Android para notificar o sistema sobre seu
WearableListenerService
. Isso permite que o sistema vincule o serviço conforme necessário.
O exemplo abaixo mostra como implementar um WearableListenerService
simples:
Kotlin
private const val TAG = "DataLayerSample" private const val START_ACTIVITY_PATH = "/start-activity" private const val DATA_ITEM_RECEIVED_PATH = "/data-item-received" class DataLayerListenerService : WearableListenerService() { override fun onDataChanged(dataEvents: DataEventBuffer) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "onDataChanged: $dataEvents") } // Loop through the events and send a message // to the node that created the data item. dataEvents.map { it.dataItem.uri } .forEach { uri -> // Get the node ID from the host value of the URI. val nodeId: String = uri.host // Set the data of the message to be the bytes of the URI. val payload: ByteArray = uri.toString().toByteArray() // Send the RPC. Wearable.getMessageClient(this) .sendMessage(nodeId, DATA_ITEM_RECEIVED_PATH, payload) } } }
Java
public class DataLayerListenerService extends WearableListenerService { private static final String TAG = "DataLayerSample"; private static final String START_ACTIVITY_PATH = "/start-activity"; private static final String DATA_ITEM_RECEIVED_PATH = "/data-item-received"; @Override public void onDataChanged(DataEventBuffer dataEvents) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "onDataChanged: " + dataEvents); } // Loop through the events and send a message // to the node that created the data item. for (DataEvent event : dataEvents) { Uri uri = event.getDataItem().getUri(); // Get the node ID from the host value of the URI. String nodeId = uri.getHost(); // Set the data of the message to be the bytes of the URI. byte[] payload = uri.toString().getBytes(); // Send the RPC. Wearable.getMessageClient(this).sendMessage( nodeId, DATA_ITEM_RECEIVED_PATH, payload); } } }
A próxima seção explica como usar um filtro de intent com esse listener.
Usar filtros com o WearableListenerService
Um filtro de intent para o exemplo de WearableListenerService
apresentado na seção anterior
pode ter a seguinte aparência:
<service android:name=".DataLayerListenerService" android:exported="true" tools:ignore="ExportedService" > <intent-filter> <action android:name="com.google.android.gms.wearable.DATA_CHANGED" /> <data android:scheme="wear" android:host="*" android:path="/start-activity" /> </intent-filter> </service>
Nesse filtro, a ação DATA_CHANGED
substitui a
BIND_LISTENER
, previamente recomendada, para que apenas eventos específicos
ativem ou iniciem o app. Essa mudança melhora a eficiência do sistema
e reduz o consumo da bateria e outras sobrecargas associadas ao
app. Nesse exemplo, o smartwatch detecta o
item de dados /start-activity
, e o
smartphone detecta a resposta de mensagem /data-item-received
.
As regras padrão de correspondência de filtros Android são aplicáveis. É possível especificar vários serviços
por manifesto, vários filtros de intent por serviço, várias ações por filtro
e várias estrofes de dados por filtro. Os filtros podem corresponder a um host curinga ou
específico. Para fazer correspondência com um host curinga, use host="*"
. Para fazer
correspondência com um host específico, especifique host=<node_id>
.
Também é possível fazer correspondência com um caminho literal ou prefixo de caminho. Para isso, é necessário definir um caractere curinga ou um host específico. Caso contrário, o caminho definido vai ser ignorado pelo sistema.
Para saber mais sobre os tipos de filtro com suporte no Wear OS, consulte a
documentação de referência da API para
WearableListenerService
.
Para saber mais sobre filtros de dados e regras de correspondência, consulte a documentação de referência da API
para o
elemento de manifesto <data>
.
Tenha em mente duas regras importantes ao fazer a correspondência de filtros de intent:
- Se nenhum esquema for especificado para o filtro de intent, o sistema vai ignorar todos os outros atributos de URI.
- Se nenhum host for especificado para o filtro, todos os atributos do caminho serão ignorados pelo sistema.
Usar um listener em tempo real
Caso o app se importe com eventos de camada de dados somente quando o usuário estiver interagindo com ele, pode não ser necessário ter um serviço de execução longa para processar cada alteração de dados. Nesse caso, é possível ouvir eventos em uma atividade implementando uma ou mais das interfaces abaixo:
DataClient.OnDataChangedListener
MessageClient.OnMessageReceivedListener
CapabilityClient.OnCapabilityChangedListener
ChannelClient.ChannelCallback
Para criar uma atividade que ouve eventos de dados, faça o seguinte:
- Implemente as interfaces desejadas.
- No método
onCreate()
ouonResume()
, chameWearable.getDataClient(this).addListener()
,MessageClient.addListener()
,CapabilityClient.addListener()
ouChannelClient.registerChannelCallback()
para informar ao Google Play Services que a atividade precisa detectar eventos da camada de dados. - Em
onStop()
ouonPause()
, cancele o registro de listeners comDataClient.removeListener()
,MessageClient.removeListener()
,CapabilityClient.removeListener()
ouChannelClient.unregisterChannelCallback()
. - Caso uma atividade só precise detectar eventos com um prefixo de caminho específico, adicione um listener com um filtro de prefixos para receber apenas dados relevantes ao status do aplicativo.
- Implemente
onDataChanged()
,onMessageReceived()
,onCapabilityChanged()
ou métodos deChannelClient.ChannelCallback
, dependendo das interfaces que você implementou. Esses métodos são chamados na linha de execução principal. Também é possível especificar umLooper
personalizado usandoWearableOptions
.
Veja um exemplo que implementa DataClient.OnDataChangedListener
:
Kotlin
class MainActivity : Activity(), DataClient.OnDataChangedListener { public override fun onResume() { Wearable.getDataClient(this).addListener(this) } override fun onPause() { Wearable.getDataClient(this).removeListener(this) } override fun onDataChanged(dataEvents: DataEventBuffer) { dataEvents.forEach { event -> if (event.type == DataEvent.TYPE_DELETED) { Log.d(TAG, "DataItem deleted: " + event.dataItem.uri) } else if (event.type == DataEvent.TYPE_CHANGED) { Log.d(TAG, "DataItem changed: " + event.dataItem.uri) } } } }
Java
public class MainActivity extends Activity implements DataClient.OnDataChangedListener { @Override public void onResume() { Wearable.getDataClient(this).addListener(this); } @Override protected void onPause() { Wearable.getDataClient(this).removeListener(this); } @Override public void onDataChanged(DataEventBuffer dataEvents) { for (DataEvent event : dataEvents) { if (event.getType() == DataEvent.TYPE_DELETED) { Log.d(TAG, "DataItem deleted: " + event.getDataItem().getUri()); } else if (event.getType() == DataEvent.TYPE_CHANGED) { Log.d(TAG, "DataItem changed: " + event.getDataItem().getUri()); } } } }
Usar filtros com listeners em tempo real
Como mencionado anteriormente, assim como é possível especificar filtros de intent para
objetos WearableListenerService
do manifesto,
também é possível usar filtros de intent ao registrar um listener em tempo real usando a
API
Wearable. As mesmas regras se aplicam aos listeners em tempo real da API
e aos listeners do manifesto.
Um padrão comum é registrar um listener com um caminho específico ou prefixo de caminho
no método onResume()
de uma atividade e
remover o listener do método onPause()
dessa atividade.
Implementar listeners dessa maneira permite que o app receba eventos
de forma mais seletiva, melhorando o design e a eficiência.