A partir de la versión 11.8.0 de los Servicios de Google Play, se deben migrar las apps para Wear OS de la clase GoogleApiClient
y usar, en cambio, objetos de clientes basados en la clase
GoogleApi
.
El uso de
GoogleApi
facilita la configuración de operaciones asíncronas.
Por ejemplo, como se describe en la introducción a la API de Tasks, puedes obtener un objeto Task
en lugar de un objeto
PendingResult
.
En esta página, se incluye lo siguiente:
- Una tabla de componentes de repuesto
- Un ejemplo de actualización de una app existente para usar la API de Tasks
Nota: Esta actualización no se aplica a las apps de Wear OS para China, que generalmente usan la versión 10.2.0 de los Servicios de Google Play.
Nota: Por el momento, esta API solo está disponible en teléfonos Android y relojes Wear OS vinculados a teléfonos Android. En el caso de los relojes con Wear OS vinculados a teléfonos iOS, las apps pueden consultar otras APIs basadas en la nube si hay conectividad a Internet disponible.
Reemplazos para componentes obsoletos
Cuando usas clases que extienden la clase
GoogleApi
, como
DataClient
y
MessageClient
, el SDK de Servicios de Google Play administra por ti las conexiones a estos servicios.
Las apps que usan las clases de reemplazo que se indican a continuación no necesitan crear y administrar objetos GoogleApiClient
. Consulta también Cómo acceder a las APIs de Google y la página de referencia para la clase Wearable.
En la siguiente tabla, se incluyen los componentes que dejaron de estar disponibles y sus reemplazos:
Componente obsoleto | Componente de reemplazo |
CapabilityApi
|
CapabilityClient
|
Channel
|
ChannelClient.Channel
|
ChannelApi
|
ChannelClient
|
DataApi
|
DataClient
|
MessageApi
|
MessageClient
|
NodeApi
|
NodeClient
|
Además, ten en cuenta lo siguiente:
- Para las notificaciones de cambios en los canales, se reemplaza
Channel.ChannelListener
porChannelClient.ChannelCallback
. - Para configurar el subproceso de devoluciones de llamada del agente de escucha, se reemplaza
GoogleApiClient.Builder.setHandler
por el métodosetLooper
deWearableOptions.Builder
.
Ejemplo de migración de una app para Wear
Como ejemplo de migración, en los fragmentos de código a continuación, puedes ver la actualización de la muestra de Data Layer de Wear, que usa la API de Data Layer, para la versión 11.8.0 de los Servicios de Google Play. Si tu app tiene un módulo de teléfono, es posible que sus actualizaciones sean similares a las del módulo de Wear.
Cómo actualizar la dependencia de los Servicios de Google Play
Debido a que es posible que tu app dependa de una versión anterior de los Servicios de Google Play, actualiza la siguiente dependencia en el archivo build.gradle
de tu módulo de Wear:
dependencies { ... compile 'com.google.android.gms:play-services-wearable:11.8.0' }
Cómo actualizar las declaraciones de importación de tu app
Importa las clases necesarias, incluidas las clases en la API de Tasks.
Por ejemplo, antes la muestra de Data Layer de Wear incluía la siguiente sentencia de importación en el archivo MainActivity.java
. Se debería quitar esta sentencia de import
:
Kotlin
... import com.google.android.gms.common.api.GoogleApiClient ...
Java
... import com.google.android.gms.common.api.GoogleApiClient; ...
En la muestra de Data Layer de Wear, se reemplazaron las declaraciones de import
como la anterior, por ejemplo, con la siguiente (la segunda sirve para administrar excepciones de tareas):
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; ...
Cómo implementar las nuevas interfaces de cliente
Quita cualquier uso de la clase GoogleApiClient
y las interfaces asociadas (ConnectionCallbacks
, OnConnectionFailedListener
, etc.) y reemplaza las otras implementaciones de objetos de escucha con sus versiones nuevas. Por lo general, los métodos reales para anular tienen el mismo nombre que antes, de manera que el cambio principal es similar al ejemplo que se muestra a continuación.
La actividad principal de la muestra de Data Layer de Wear (como se indica en un informe sobre diferencias en GitHub) había implementado, por ejemplo, la interfaz de CapabilityApi.CapabilityListener
. Sin embargo, ahora la actividad principal implementa CapabilityClient.OnCapabilityChangedListener
.
A continuación, se muestra una comparación de las definiciones de clase.
A continuación, puedes ver un fragmento antes del uso de la versión 11.8.0 de los Servicios de Google Play:
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
Aquí puedes ver un fragmento después del uso de la versión 11.8.0 de los Servicios de Google Play:
Kotlin
class MainActivity : Activity(), DataClient.OnDataChangedListener, MessageClient.OnMessageReceivedListener, CapabilityClient.OnCapabilityChangedListener
Java
public class MainActivity extends Activity implements DataClient.OnDataChangedListener, MessageClient.OnMessageReceivedListener, CapabilityClient.OnCapabilityChangedListener
Cómo quitar y agregar objetos de escucha
Los objetos de cliente nuevos se almacenan en caché y se comparten entre las instancias de
GoogleApi
. Por lo tanto, no es necesario mantener variables de los miembros; no es costoso crear los clientes, y estos no perderán sus objetos de escucha.
A continuación, se muestra un fragmento de la muestra de Data Layer de Wear revisada:
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); }
Cómo solicitar información con la API de Tasks
Es posible que desees solicitar información fuera de los objetos de escucha que actualizan tu app cuando cambian los datos. En estos casos, realiza una solicitud en la que se use un cliente como
DataClient
, además de la API de Tasks y una clase de resultado (es decir, como Task<ResultType>
).
Por ejemplo, como puedes ver en la muestra de Data Layer de Wear, puedes usar la API de Tasks para encontrar nodos conectados con cualquier función:
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(); }
Si deseas obtener un código adicional que usa las APIs de Wearable y Tasks, consulta la muestra de Data Layer de Wear. Además, como ejemplo del uso de tareas pesadas del subproceso de IU o en un servicio, hay otra opción disponible. A continuación, puedes ver un ejemplo para bloquear una tarea y obtener el resultado de manera 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); } }