Na versão
11.8.0 e mais recentes do Google Play Services, os apps para Wear OS vão abandonar o uso
da classe GoogleApiClient
e, no lugar dela, vão usar objetos de cliente baseados na classe
GoogleApi
.
O uso da
GoogleApi
facilita a configuração de operações assíncronas.
Por exemplo, conforme descrito na introdução à
API Tasks, você pode extrair um objeto Task
em vez de um
objeto
PendingResult
.
Esta página inclui:
- Uma tabela de componentes de substituição
- Um exemplo de atualização de um app já existente para usar a API Tasks
Observação: essa atualização não se aplica a apps para Wear OS da China, que geralmente usam a versão 10.2.0 do Google Play Services.
Observação: no momento, essa API está disponível apenas em smartphones Android e smartwatches Wear OS pareados com smartphones Android. Para smartwatches Wear OS pareados com smartphones iOS, os apps podem consultar outras APIs baseadas na nuvem se a conectividade com a Internet estiver disponível.
Substituições de componentes descontinuados
Quando você usa classes que estendem a classe
GoogleApi
, por exemplo,
DataClient
e
MessageClient
, o SDK do Google Play Services gerencia
conexões do Google Play Services para você.
Os apps que usam as classes de substituição abaixo não precisam criar e
gerenciar objetos
GoogleApiClient
. Consulte também Como acessar
APIs do Google e a página
de referência da classe Wearable.
A tabela abaixo contém componentes descontinuados e as respectivas substituições.
Componente descontinuado | Componente substituto |
CapabilityApi
|
CapabilityClient
|
Channel
|
ChannelClient.Channel
|
ChannelApi
|
ChannelClient
|
DataApi
|
DataClient
|
MessageApi
|
MessageClient
|
NodeApi
|
NodeClient
|
Observe também o seguinte:
- Para notificações de mudanças em canais, o
Channel.ChannelListener
é substituído porChannelClient.ChannelCallback
- Para definir a linha de execução para callbacks de listener, o
GoogleApiClient.Builder.setHandler
é substituído pelo métodosetLooper
doWearableOptions.Builder
Exemplo de migração para um app Wear
Como exemplo de migração, os snippets de código abaixo ilustram como o exemplo da Data Layer do Wear (link em inglês), que usa a API Data Layer, foi atualizada para a versão 11.8.0 do Google Play Services. Caso seu app tenha um módulo de telefone, as atualizações dele podem ser semelhantes às do módulo Wear.
Atualizar a dependência do Google Play Services
Como seu app pode depender de uma versão anterior do Google Play Services,
atualize a dependência abaixo no arquivo build.gradle
do
seu módulo Wear:
dependencies { ... compile 'com.google.android.gms:play-services-wearable:11.8.0' }
Atualizar as declarações de importação do seu app
Importe as classes necessárias, incluindo as classes na API Tasks.
Por exemplo, o exemplo da Data Layer
do Wear (link em inglês) costumava incluir a declaração de importação abaixo no
arquivo MainActivity.java
. Essa declaração import
precisa ser removida:
Kotlin
... import com.google.android.gms.common.api.GoogleApiClient ...
Java
... import com.google.android.gms.common.api.GoogleApiClient; ...
No exemplo
da Data Layer do Wear (link em inglês), as declarações de import
, como a mostrada acima,
foram substituídas, por exemplo, pela mostrada abaixo (a segunda é para
gerenciar exceções de tarefas):
Kotlin
... import com.google.android.gms.tasks.Tasks import java.util.concurrent.ExecutionException ...
Java
... import com.google.android.gms.tasks.Tasks; import java.util.concurrent.ExecutionException; ...
Implementar as novas interfaces do cliente
Remova todos os usos da classe GoogleApiClient
e as interfaces associadas (ConnectionCallbacks
,
OnConnectionFailedListener
etc.) e substitua as outras
implementações do listener pelas novas versões. Os métodos de
substituição costumam ter os mesmos nomes de antes. A mudança principal é
semelhante ao exemplo abaixo.
A principal atividade do exemplo da Data Layer do Wear (como indicado em um relatório de diferenças
no
GitHub, link em inglês) implementou, por exemplo, a
interface CapabilityApi.CapabilityListener
. No entanto, a
atividade principal implementa
CapabilityClient.OnCapabilityChangedListener
.
Confira abaixo uma comparação das definições de classe.
Confira um snippet antes de usar a versão 11.8.0 do Google Play Services:
Kotlin
class MainActivity : Activity(), GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, DataApi.DataListener, MessageApi.MessageListener, CapabilityApi.CapabilityListener
Java
public class MainActivity extends Activity implements ConnectionCallbacks, OnConnectionFailedListener, DataApi.DataListener, MessageApi.MessageListener, CapabilityApi.CapabilityListener
Confira um snippet depois de usar a versão 11.8.0 do Google Play Services:
Kotlin
class MainActivity : Activity(), DataClient.OnDataChangedListener, MessageClient.OnMessageReceivedListener, CapabilityClient.OnCapabilityChangedListener
Java
public class MainActivity extends Activity implements DataClient.OnDataChangedListener, MessageClient.OnMessageReceivedListener, CapabilityClient.OnCapabilityChangedListener
Remover e adicionar listeners
Os novos objetos do cliente são armazenados em cache e compartilhados entre instâncias de
GoogleApi
, então não é necessário manter variáveis de
membro. Os clientes têm baixo custo de criação e não perdem os
listeners.
Confira abaixo um snippet do exemplo revisado da Data Layer do Wear (link em inglês):
Kotlin
override fun onResume() { super.onResume() Wearable.getDataClient(this).addListener(this) Wearable.getMessageClient(this).addListener(this) Wearable.getCapabilityClient(this) .addListener( this, Uri.parse("wear://"), CapabilityClient.FILTER_REACHABLE ) } override fun onPause() { super.onPause() Wearable.getDataClient(this).removeListener(this) Wearable.getMessageClient(this).removeListener(this) Wearable.getCapabilityClient(this).removeListener(this) }
Java
@Override protected void onResume() { super.onResume(); Wearable.getDataClient(this).addListener(this); Wearable.getMessageClient(this).addListener(this); Wearable.getCapabilityClient(this) .addListener( this, Uri.parse("wear://"), CapabilityClient.FILTER_REACHABLE); } @Override protected void onPause() { super.onPause(); Wearable.getDataClient(this).removeListener(this); Wearable.getMessageClient(this).removeListener(this); Wearable.getCapabilityClient(this).removeListener(this); }
Solicitar informações com a API Tasks
Você pode solicitar informações de fora dos listeners que atualizam
seu app quando há uma mudança nos dados. Nesses casos, faça uma solicitação usando
um cliente como o
DataClient
em conjunto com a API Tasks e
uma classe de resultado (por exemplo, Task<ResultType>
).
Por exemplo, como mostrado no exemplo da Data Layer do Wear (link em inglês), você pode usar a API Tasks para encontrar nós conectados com determinados recursos:
Kotlin
private fun showNodes(vararg capabilityNames: String) { Wearable.getCapabilityClient(this) .getAllCapabilities(CapabilityClient.FILTER_REACHABLE).apply { addOnSuccessListener { capabilityInfoMap -> val nodes: Set<Node> = capabilityInfoMap .filter { capabilityNames.contains(it.key) } .flatMap { it.value.nodes } .toSet() showDiscoveredNodes(nodes) } } } private fun showDiscoveredNodes(nodes: Set<Node>) { val nodesList: Set<String> = nodes.map { it.displayName }.toSet() val msg: String = if (nodesList.isEmpty()) { Log.d(TAG, "Connected Nodes: No connected device was found for the given capabilities") getString(R.string.no_device) } else { Log.d(TAG, "Connected Nodes: ${nodesList.joinToString(separator = ", ")}") getString(R.string.connected_nodes, nodesList) } Toast.makeText(this@MainActivity, msg, Toast.LENGTH_LONG).show() }
Java
private void showNodes(final String... capabilityNames) { Task<Map<String, CapabilityInfo>> capabilitiesTask = Wearable.getCapabilityClient(this) .getAllCapabilities(CapabilityClient.FILTER_REACHABLE); capabilitiesTask.addOnSuccessListener(new OnSuccessListener<Map<String, CapabilityInfo>>() { @Override public void onSuccess(Map<String, CapabilityInfo> capabilityInfoMap) { Set<Node> nodes = new HashSet<>(); if (capabilityInfoMap.isEmpty()) { showDiscoveredNodes(nodes); return; } for (String capabilityName : capabilityNames) { CapabilityInfo capabilityInfo = capabilityInfoMap.get(capabilityName); if (capabilityInfo != null) { nodes.addAll(capabilityInfo.getNodes()); } } showDiscoveredNodes(nodes); } }); } private void showDiscoveredNodes(Set<Node> nodes) { List<String> nodesList = new ArrayList<>(); for (Node node : nodes) { nodesList.add(node.getDisplayName()); } LOGD(TAG, "Connected Nodes: " + (nodesList.isEmpty() ? "No connected device was found for the given capabilities" : TextUtils.join(",", nodesList))); String msg; if (!nodesList.isEmpty()) { msg = getString(R.string.connected_nodes, TextUtils.join(", ", nodesList)); } else { msg = getString(R.string.no_device); } Toast.makeText(MainActivity.this, msg, Toast.LENGTH_LONG).show(); }
Para conferir um outro código que utiliza as APIs Wearable e Tasks, consulte o exemplo da Data Layer do Wear (link em inglês). E, como um exemplo de uso de tarefas pesadas fora da linha de execução de interface ou em um serviço, há outra opção disponível. Confira este exemplo de como bloquear uma tarefa e extrair o resultado de maneira síncrona:
Kotlin
override fun doInBackground(vararg params: Asset): Bitmap? { if (params.isNotEmpty()) { val asset = params[0] val getFdForAssetResponseTask: Task<DataClient.GetFdForAssetResponse> = Wearable.getDataClient(applicationContext).getFdForAsset(asset) return try { // Block on a task and get the result synchronously. This is generally done // when executing a task inside a separately managed background thread. Doing // this on the main (UI) thread can cause your application to become // unresponsive. val getFdForAssetResponse: DataClient.GetFdForAssetResponse = Tasks.await(getFdForAssetResponseTask) getFdForAssetResponse.inputStream?.let { assetInputStream -> BitmapFactory.decodeStream(assetInputStream) } ?: run { Log.w(TAG, "Requested an unknown Asset.") null } } catch (exception: ExecutionException) { Log.e(TAG, "Failed retrieving asset, Task failed: $exception") return null } catch (exception: InterruptedException) { Log.e(TAG, "Failed retrieving asset, interrupt occurred: $exception") return null } } else { Log.e(TAG, "Asset must be non-null") return null } } override fun onPostExecute(bitmap: Bitmap?) { bitmap?.also { Log.d(TAG, "Setting background image on second page..") moveToPage(1) assetFragment.setBackgroundImage(it) } }
Java
@Override protected Bitmap doInBackground(Asset... params) { if (params.length > 0) { Asset asset = params[0]; Task<DataClient.GetFdForAssetResponse> getFdForAssetResponseTask = Wearable.getDataClient(getApplicationContext()).getFdForAsset(asset); try { // Block on a task and get the result synchronously. This is generally done // when executing a task inside a separately managed background thread. Doing // this on the main (UI) thread can cause your application to become // unresponsive. DataClient.GetFdForAssetResponse getFdForAssetResponse = Tasks.await(getFdForAssetResponseTask); InputStream assetInputStream = getFdForAssetResponse.getInputStream(); if (assetInputStream != null) { return BitmapFactory.decodeStream(assetInputStream); } else { Log.w(TAG, "Requested an unknown Asset."); return null; } } catch (ExecutionException exception) { Log.e(TAG, "Failed retrieving asset, Task failed: " + exception); return null; } catch (InterruptedException exception) { Log.e(TAG, "Failed retrieving asset, interrupt occurred: " + exception); return null; } } else { Log.e(TAG, "Asset must be non-null"); return null; } } @Override protected void onPostExecute(Bitmap bitmap) { if (bitmap != null) { LOGD(TAG, "Setting background image on second page.."); moveToPage(1); assetFragment.setBackgroundImage(bitmap); } }