Google se compromete a impulsar la igualdad racial para las comunidades afrodescendientes. Obtén información al respecto.

Acceso a la red y sincronización en Wear

Con Wear OS by Google, un reloj se puede comunicar con una red directamente sin acceder a un teléfono con Android o iOS. Este acceso directo a la red reemplaza el uso (en Wear 1.x) de la API de Data Layer para conectarse a una red.

Acceso a la red

Las apps para Wear OS pueden realizar solicitudes de red. Cuando un reloj tiene una conexión Bluetooth a un teléfono, el tráfico de red del reloj en general se envía mediante proxy a través del teléfono. Sin embargo, cuando un teléfono no está disponible, se usan las redes Wi-Fi y móviles, según el hardware. La plataforma de Wear controla las transiciones entre redes.

Puedes usar protocolos como HTTP, TCP y UDP. No obstante, las API de android.webkit (incluida la clase CookieManager) no están disponibles. Puedes usar cookies mediante la lectura y escritura de encabezados en solicitudes y respuestas.

Además, recomendamos usar lo siguiente:

  • La API de JobScheduler para tareas asíncronas, incluido el sondeo en intervalos regulares (proceso que se describe más abajo)
  • API para varias redes si necesitas conectarte a tipos específicos de red (consulta Varias conexiones de red)

Acceso a redes de alto ancho de banda

La plataforma de Wear OS administra la conexión de red con el objetivo de proporcionar la mejor experiencia del usuario en general. Para elegir la red activa predeterminada, la plataforma evalúa dos factores:

  • La necesidad de conservar batería
  • La necesidad de ancho de banda de red

Cuando se prioriza la conservación de la batería, la red activa podría no llegar a satisfacer la necesidad de ancho de banda de ciertas tareas, como la transferencia de archivos grandes o la transmisión de contenido multimedia.

En esta sección, verás una guía para usar la clase ConnectivityManager y asegurarte de que tu app pueda usar el ancho de banda de red necesario. A fin de obtener información general sobre el control preciso de recursos de red, consulta Cómo administrar el uso de red.

Cómo adquirir una red de alto ancho de banda

En Wear OS, no debes dar por sentado que siempre hay una red de ancho de banda alto disponible. En los casos de uso en los que se requiere acceso a redes de ancho de banda alto, como la transferencia de archivos grandes o la transmisión de contenido multimedia, recomendamos que sigas estos pasos:

  1. Busca una red activa y, si la encuentras, comprueba su ancho de banda.
  2. Si no encuentras una red activa, o si su ancho de banda es insuficiente, solicita acceso a una red no medida Wi-Fi o móvil.

Puedes usar la clase ConnectivityManager para comprobar si existe una red activa y si tiene ancho de banda suficiente:

Kotlin

const val MIN_BANDWIDTH_KBPS = 320
connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val bandwidth: Int = connectivityManager.activeNetwork?.let { activeNetwork ->
    connectivityManager.getNetworkCapabilities(activeNetwork).linkDownstreamBandwidthKbps
} ?: -1

when {
    bandwidth < 0 -> {
        // No active network
    }
    bandwidth in (0 until MIN_BANDWIDTH_KBPS) -> {
        // Request a high-bandwidth network
    }
    else -> {
        // You already are on a high-bandwidth network, so start your network request
    }
}

Java

int MIN_BANDWIDTH_KBPS = 320;
connectivityManager =
  (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
Network activeNetwork = connectivityManager.getActiveNetwork();

if (activeNetwork != null) {
  int bandwidth =
    connectivityManager.getNetworkCapabilities(activeNetwork).getLinkDownstreamBandwidthKbps();

  if (bandwidth < MIN_BANDWIDTH_KBPS) {
    // Request a high-bandwidth network
  } else {
    // You already are on a high-bandwidth network, so start your network request
  }
} else {
  // No active network
}

Puedes solicitar una red no medida de ancho de banda alto con ConnectivityManager. Basta con una solicitud para obtener una red no medida Wi-Fi o móvil. Cuando la red está lista (p. ej., la radio Wi-Fi del dispositivo se conecta a una red guardada), se llama al método onAvailable() de tu instancia de NetworkCallback. Si no se encuentra una red adecuada, no se llama al método onAvailable(). Por lo tanto, deberías agotar manualmente el tiempo de espera de tu solicitud. Consulta Espera a que una red esté disponible.

Kotlin

networkCallback = object : ConnectivityManager.NetworkCallback() {
    override fun onAvailable(network: Network) {
        if (bindProcessToNetwork(network)) {
            // socket connections will now use this network
        } else {
            // app doesn't have android.permission.INTERNET permission
        }
    }
}

val request: NetworkRequest = NetworkRequest.Builder().run {
    addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
    addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
    addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
    addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
    build()
}

connectivityManager.requestNetwork(request, networkCallback)

Java

networkCallback = new ConnectivityManager.NetworkCallback() {
  @Override
  public void onAvailable(Network network) {
    if (bindProcessToNetwork(network)) {
      // socket connections will now use this network
    } else {
      // app doesn't have android.permission.INTERNET permission
    }
  }
};

NetworkRequest request = new NetworkRequest.Builder()
  .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
  .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
  .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
  .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
  .build();

connectivityManager.requestNetwork(request, networkCallback);

Libera la red

Cuando tu app ya no necesita la red de ancho de banda alto, debes liberarla con la clase ConnectivityManager para asegurarte de que la plataforma pueda reanudar la administración del acceso a la red.

Kotlin

connectivityManager.bindProcessToNetwork(null)
connectivityManager.unregisterNetworkCallback(networkCallback)

Java

connectivityManager.bindProcessToNetwork(null);
connectivityManager.unregisterNetworkCallback(networkCallback);

A fin de optimizar el consumo de la batería, una conexión de red solo debe permanecer registrada mientras se desarrolle la actividad. Por lo tanto, considera liberar la red en el método onStop() de tu actividad.

Espera a que una red esté disponible

Es posible que adquirir una red no sea un proceso instantáneo porque la radio móvil o Wi-Fi de un reloj podría estar desactivada para conservar la batería. Además, si un reloj no se puede conectar a una red, no se llamará al método onAvailable() de tu instancia de NetworkCallback. Por lo tanto, deberías agotar el tiempo de espera de la solicitud luego de un período predeterminado y liberar los recursos asociados que correspondan.

Kotlin

const val MESSAGE_CONNECTIVITY_TIMEOUT = 1
const val NETWORK_CONNECTIVITY_TIMEOUT_MS: Long = 10000
...
handler = MyHandler()

networkCallback = object : ConnectivityManager.NetworkCallback() {
    override fun onAvailable(network: Network) {
        handler.removeMessages(MESSAGE_CONNECTIVITY_TIMEOUT)
        ...
    }
}

connectivityManager.requestNetwork(request, networkCallback)

handler.sendMessageDelayed(
        handler.obtainMessage(MESSAGE_CONNECTIVITY_TIMEOUT),
        NETWORK_CONNECTIVITY_TIMEOUT_MS
)
...
// Don't make this an inner class otherwise there is the potential to leak the parent class
private class MyHandler : Handler() {
    override fun handleMessage(msg: Message) {
        when (msg.what) {
            MESSAGE_CONNECTIVITY_TIMEOUT -> {
                // unregister the network
            }
        }
    }
}

Java

int MESSAGE_CONNECTIVITY_TIMEOUT = 1;
long NETWORK_CONNECTIVITY_TIMEOUT_MS = 10000;

handler = new MyHandler();

networkCallback = new ConnectivityManager.NetworkCallback() {
  @Override
  public void onAvailable(Network network) {
    handler.removeMessages(MESSAGE_CONNECTIVITY_TIMEOUT);
    ...
  }
};

connectivityManager.requestNetwork(request, networkCallback);

handler.sendMessageDelayed(
  handler.obtainMessage(MESSAGE_CONNECTIVITY_TIMEOUT),
  NETWORK_CONNECTIVITY_TIMEOUT_MS);

...
// This needs to be static to avoid potentially leaking the parent class
private static class MyHandler extends Handler {
   @Override
   public void handleMessage(Message msg) {
       switch (msg.what) {
           case MESSAGE_CONNECTIVITY_TIMEOUT:
               // unregister the network
               break;
       }
   }
}

Controla el estado de la red

La interfaz NetworkCallback cuenta con métodos para controlar los cambios de estado de la red vinculada, como las modificaciones en el ancho de banda y la pérdida de conectividad.

Kotlin

networkCallback = object : ConnectivityManager.NetworkCallback() {
    override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
        val bandwidth: Int = networkCapabilities.linkDownstreamBandwidthKbps

        if (bandwidth < MIN_BANDWIDTH_KBPS) {
            // handle insufficient network bandwidth
        }
    }

    override fun onLost(network: Network) {
        // handle network loss
    }
}

Java

networkCallback = ConnectivityManager.NetworkCallback {
  @Override
  public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
    int bandwidth = networkCapabilities.getLinkDownstreamBandwidthKbps();

      if (bandwidth < MIN_BANDWIDTH_KBPS) {
        // handle insufficient network bandwidth
      }
  }

  @Override
  public void onLost(Network network) {
    // handle network loss
  }
}

Inicia la actividad de configuración de Wi-Fi

Cuando se solicita una red Wi-Fi, el sistema intenta conectarse a una red guardada si se había configurado alguna y si está dentro del alcance. No obstante, si no hay una red Wi-Fi disponible, no se llamará al método de devolución de llamada onAvailable() de tu instancia de NetworkCallback. Si usas un Handler a los efectos de agotar el tiempo de espera de la solicitud de red, puedes indicarle al usuario que agregue una red Wi-Fi cuando haya transcurrido dicho tiempo. Puedes enviar al usuario directamente a la actividad para agregar una red Wi-Fi con el siguiente intent:

Kotlin

context.startActivity(Intent("com.google.android.clockwork.settings.connectivity.wifi.ADD_NETWORK_SETTINGS"))

Java

context.startActivity(new Intent("com.google.android.clockwork.settings.connectivity.wifi.ADD_NETWORK_SETTINGS"));

Para iniciar la actividad de configuración, tu app debe tener el siguiente permiso: android.permission.CHANGE_WIFI_STATE.

Consideraciones sobre la interfaz de usuario

Si tu app requiere una conexión a una nueva red Wi-Fi para una operación de ancho de banda alto, asegúrate de que el motivo de la conexión sea claro para el usuario antes de que inicies la configuración de Wi-Fi. Solo debes solicitar que el usuario agregue una nueva red Wi-Fi cuando se requiera la red de ancho de banda alto. No bloquees el acceso de un usuario a las funciones de la app que no necesitan una red de ancho de banda alto.

En la figura 1, se muestra, por ejemplo, una app de música. La app debería permitirle al usuario explorar música y solicitarle que agregue una nueva red Wi-Fi únicamente si quiere descargar o transmitir música.

Descarga de música

Figura 1: El flujo de una app de música para descargar contenido musical

Si tu app requiere una red de ancho de banda alto de modo que funcione, debes presentar una explicación clara al usuario antes de pedirle que agregue una nueva red Wi-Fi. Además, para operaciones de red prolongadas, como descargar la playlist de contenido multimedia de un usuario, debes mostrar un indicador de progreso con una descripción de la operación que se está realizando.

En la figura 2, se muestra la app de música en un flujo de transmisión de contenido musical. Si un usuario desea transmitir música y se necesita una red de alto ancho de banda, la app debe explicar claramente el motivo antes de llevar al usuario a la configuración de Wi-Fi.

Transmisión de música

Figura 2: El flujo de una app de música para transmitir contenido musical

Envío de mensajes a través de la nube

Para enviar notificaciones, las apps pueden usar directamente Firebase Cloud Messaging (FCM), que reemplaza a Google Cloud Messaging (GCM). FCM es compatible con Wear 2.0, mientras que GCM no.

No hay API para acceso a redes o a FCM que sean específicas de Wear OS. Consulta la documentación existente sobre cómo conectarse a una red y sobre el envío de mensajes a través de la nube.

FCM funciona bien con Descanso y es el método recomendado para enviar notificaciones a un reloj.

Permite los mensajes de FCM mediante la recopilación de un token de registro para un dispositivo cuando se ejecute tu app para Wear. Luego, incluye el token como parte del destino cuando tu servidor envíe mensajes al extremo REST de FCM. FCM envía mensajes al dispositivo que identificó el token.

Un mensaje de FCM está en formato JSON y puede incluir una de las siguientes cargas útiles o ambas:

  • Carga útil de la notificación: cuando un reloj recibe la carga útil de una notificación, los datos se muestran directamente al usuario en el flujo de notificaciones. Cuando el usuario presiona la notificación, se inicia tu app.
  • Carga útil de datos: la carga útil tiene un conjunto de pares clave-valor personalizados. La carga útil se entrega como datos a tu app para Wear.

Para obtener más información y ver ejemplos relacionados con cargas útiles, consulta Información sobre los mensajes de FCM.

De manera predeterminada, las notificaciones se comparten desde una aplicación para teléfonos hacia un reloj. Si tienes una app independiente para Wear y la aplicación para teléfonos correspondiente, pueden generarse notificaciones duplicadas. Por ejemplo, un teléfono y un reloj que recibieron la misma notificación de FCM podrían mostrarla de forma independiente.

Usa servicios en segundo plano

A fin de garantizar que las tareas en segundo plano se ejecuten correctamente, deben tener en cuenta la función Descanso. En Android 6.0, Descanso y App Standby permitieron mejorar la duración de batería.

Se mejoró la función Descanso en Android Nougat y Wear OS. Cuando una pantalla se apaga o ingresa al modo ambiente durante un tiempo suficientemente largo, puede producirse una instancia de Descanso y las tareas en segundo plano pueden posponerse algunos períodos. Más tarde, cuando un dispositivo permanece inactivo durante un tiempo prolongado, aparece la función Descanso normal.

Deberías programar trabajos con la API de JobScheduler, que permite que tu app se registre para ejecutar código a prueba de Descanso. Cuando programas trabajos, puedes elegir restricciones; por ejemplo, que el trabajo se ejecute periódicamente, que se requiera conectividad o que el dispositivo esté cargado. Configura trabajos de manera tal que no perjudiquen la duración de batería. Usa el objeto JobInfo.Builder en trabajos a fin de proporcionar restricciones y metadatos, p. ej., con uno o más de los siguientes métodos para una tarea:

  • A los efectos de programar una tarea que requiera una red, usa setRequiredNetworkType(int networkType) y especifica NETWORK_TYPE_ANY o NETWORK_TYPE_UNMETERED. Ten en cuenta que NETWORK_TYPE_UNMETERED se utiliza para transferencias grandes de datos, mientras que NETWORK_TYPE_ANY es para transferencias pequeñas.
  • Para programar una tarea mientras el dispositivo se carga, usa setRequiresCharging(boolean requiresCharging).
  • A fin de especificar que un dispositivo está inactivo a la hora de realizar una tarea, usa setRequiresDeviceIdle(boolean requiresDeviceIdle). Este método es útil para la sincronización o los trabajos en segundo plano de baja prioridad, en especial cuando se usa con setRequiresCharging.

Ten en cuenta que algunas redes de ancho de banda bajo, como Bluetooth de bajo consumo, se consideran de uso medido.

Programa tareas con restricciones

Puedes programar una tarea que incluya restricciones. En el ejemplo que se muestra a continuación, un objeto JobScheduler activa MyJobService cuando se cumplen las siguientes restricciones:

  • Red no medida
  • Dispositivo en carga

Puedes usar el método del compilador setExtras para adjuntar un conjunto de metadatos específicos de la app a la solicitud del trabajo. Cuando este se ejecuta, se proporciona el paquete a tu servicio de trabajo. Ten en cuenta el valor MY_JOB_ID que se pasa al constructor JobInfo.Builder. Este valor MY_JOB_ID es un identificador que proporciona la app. Las llamadas subsiguientes para cancelar y los trabajos posteriores creados con ese mismo valor actualizarán el trabajo existente:

Kotlin

JobInfo.Builder(MY_JOB_ID,
        ComponentName(this, MyJobService::class.java))
        .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
        .setRequiresCharging(true)
        .setExtras(extras)
        .build()
        .also { jobInfo ->
            (getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler)
                    .schedule(jobInfo)
        }

Java

JobInfo jobInfo = new JobInfo.Builder(MY_JOB_ID,
        new ComponentName(this, MyJobService.class))
        .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
        .setRequiresCharging(true)
        .setExtras(extras)
        .build();
((JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE))
        .schedule(jobInfo);

Más abajo encontrarás una implementación de JobService para controlar el trabajo anterior. Cuando se ejecuta el trabajo, se pasa un objeto JobParameters al método onStartJob. El objeto JobParameters te permite obtener el valor del ID de trabajo junto con cualquier paquete de extras provisto al momento de programar el trabajo. Como se llama al método onStartJob en el subproceso principal de la aplicación, se debe ejecutar cualquier lógica que consuma muchos recursos desde un subproceso separado. En el ejemplo, se usa AsyncTask para ejecutar código en segundo plano. Cuando finalice el trabajo, llamarás al método jobFinished a fin de informar a JobScheduler que se completó la tarea:

Kotlin

private class MyJobService : JobService() {

    override fun onStartJob(params: JobParameters): Boolean {
        JobAsyncTask().execute(params)
        return true
    }

    private class JobAsyncTask : AsyncTask<...>() { ... }
}

Java

public class MyJobService extends JobService {
    @Override public boolean onStartJob(JobParameters params) {
        new JobAsyncTask().execute(params);
        return true;
    }

    private class JobAsyncTask extends AsyncTask