Las solicitudes que tu app le envía a la red son una de las principales causas del agotamiento de batería, ya que activan radios celulares o Wi-Fi que consumen mucha energía. Más allá de la potencia para enviar y recibir paquetes, estas radios consumen más energía solo encenderse y mantenerse despierto. Algo tan simple como una solicitud de red cada 15 segundos puede mantener encendida la radio móvil de forma continua y agotar la batería rápidamente energía.
Existen tres tipos generales de actualizaciones regulares:
- Iniciado por el usuario. Realizar una actualización en función de algún comportamiento del usuario, como un gesto de deslizar para actualizar.
- Iniciado por la app Actualizar de forma recurrente
- Iniciado por el servidor. Realizar una actualización en respuesta a una notificación de un servidor.
En este tema, se analizan cada una de ellas y se analizan otras formas en las que se pueden usar optimizado para reducir el agotamiento de la batería.
Optimiza las solicitudes iniciadas por el usuario
Las solicitudes que inicia el usuario suelen ocurrir en respuesta a algún comportamiento del usuario. Para Por ejemplo, una aplicación que se usa para leer los artículos de noticias más recientes puede permitir que el usuario realizar un gesto de deslizar para actualizar para buscar artículos nuevos Puedes usar la seguir técnicas para responder a las solicitudes iniciadas por el usuario y, al mismo tiempo, optimizar el uso de la red.
Limita las solicitudes de los usuarios
Es posible que desees ignorar algunas solicitudes iniciadas por el usuario si no son necesarias, como varios gestos de deslizar para actualizar en un período breve para verificar si hay datos nuevos mientras los datos actuales aún son recientes. Cómo actuar en función de cada podría desperdiciar una gran cantidad de energía manteniendo la radio activa. Un enfoque más eficiente es limitar las solicitudes que inicia el usuario para que solo se pueda realizar una solicitud durante un período, lo que reduce la frecuencia con la que se usa la radio.
Usa una caché
Cuando almacenas en caché los datos de tu app, creas una copia local de la información. a los que la aplicación debe hacer referencia. Así, tu app puede acceder a la misma red local una copia de la información varias veces sin tener que abrir una red para realizar solicitudes nuevas.
Debes almacenar en caché los datos de la forma más agresiva posible, incluidos los recursos estáticos y las descargas on demand, como las imágenes de tamaño completo. Puedes usar HTTP los encabezados de caché para garantizar que tu estrategia para mostrar datos inactivos. Para obtener más información sobre el almacenamiento en caché de las respuestas de la red, consulta Cómo evitar descargas redundantes.
En Android 11 y versiones posteriores, tu app puede usar los mismos conjuntos de datos grandes que usan otras apps para casos de uso como el aprendizaje automático y la reproducción de contenido multimedia. Cuando el La app necesita acceder a un conjunto de datos compartido, primero puede buscar una versión almacenada en caché antes de intentar descargar una nueva copia. Para obtener más información sobre los conjuntos de datos compartidos, consulta Cómo acceder a conjuntos de datos compartidos.
Cómo usar mayor ancho de banda para descargar más datos con menos frecuencia
Cuando se conecta a través de una radio inalámbrica, el ancho de banda más alto suele tener un costo de batería más alto, lo que significa que, por lo general, 5G consume más energía que LTE, que a su vez es más costoso que 3G.
Esto significa que, si bien el estado de radio subyacente varía según el tecnología de radio, que en términos generales el impacto relativo de la batería del estado el tiempo de cola de cambio es mayor en radios con un ancho de banda mayor. Para obtener más información tiempo de cola; consulta El estado de radio máquina virtual.
Al mismo tiempo, un mayor ancho de banda significa que puedes precargar más de manera intensa, ya que descargan más datos al mismo tiempo. Quizás menos de manera intuitiva, debido a que el costo de la batería del tiempo de cola es relativamente mayor, es más eficiente mantener la radio activa durante períodos más largos durante cada transferencia para reducir la frecuencia de las actualizaciones.
Por ejemplo, si una radio LTE tiene el doble de ancho de banda y costo de energía que 3G, deberás descargar cuatro veces más datos durante cada sesión, o hasta 10 MB. Al descargar tantos datos, es importante considera el efecto de la carga previa en el almacenamiento local disponible tu caché de carga previa con regularidad.
Puedes usar ConnectivityManager
para registrar un objeto de escucha para la red predeterminada y TelephonyManager
para registrar un PhoneStateListener
para determinar el tipo de conexión actual del dispositivo. Una vez que conozcas el tipo de conexión,
Puedes modificar tus rutinas de carga previa según corresponda:
Kotlin
val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val tm = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager private var hasWifi = false private var hasCellular = false private var cellModifier: Float = 1f private val networkCallback = object : ConnectivityManager.NetworkCallback() { // Network capabilities have changed for the network override fun onCapabilitiesChanged( network: Network, networkCapabilities: NetworkCapabilities ) { super.onCapabilitiesChanged(network, networkCapabilities) hasCellular = networkCapabilities .hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) hasWifi = networkCapabilities .hasTransport(NetworkCapabilities.TRANSPORT_WIFI) } } private val phoneStateListener = object : PhoneStateListener() { override fun onPreciseDataConnectionStateChanged( dataConnectionState: PreciseDataConnectionState ) { cellModifier = when (dataConnectionState.networkType) { TelephonyManager.NETWORK_TYPE_LTE or TelephonyManager.NETWORK_TYPE_HSPAP -> 4f TelephonyManager.NETWORK_TYPE_EDGE or TelephonyManager.NETWORK_TYPE_GPRS -> 1/2f else -> 1f } } private class NetworkState { private var defaultNetwork: Network? = null private var defaultCapabilities: NetworkCapabilities? = null fun setDefaultNetwork(network: Network?, caps: NetworkCapabilities?) = synchronized(this) { defaultNetwork = network defaultCapabilities = caps } val isDefaultNetworkWifi get() = synchronized(this) { defaultCapabilities?.hasTransport(TRANSPORT_WIFI) ?: false } val isDefaultNetworkCellular get() = synchronized(this) { defaultCapabilities?.hasTransport(TRANSPORT_CELLULAR) ?: false } val isDefaultNetworkUnmetered get() = synchronized(this) { defaultCapabilities?.hasCapability(NET_CAPABILITY_NOT_METERED) ?: false } var cellNetworkType: Int = TelephonyManager.NETWORK_TYPE_UNKNOWN get() = synchronized(this) { field } set(t) = synchronized(this) { field = t } private val cellModifier: Float get() = synchronized(this) { when (cellNetworkType) { TelephonyManager.NETWORK_TYPE_LTE or TelephonyManager.NETWORK_TYPE_HSPAP -> 4f TelephonyManager.NETWORK_TYPE_EDGE or TelephonyManager.NETWORK_TYPE_GPRS -> 1 / 2f else -> 1f } } val prefetchCacheSize: Int get() = when { isDefaultNetworkWifi -> MAX_PREFETCH_CACHE isDefaultNetworkCellular -> (DEFAULT_PREFETCH_CACHE * cellModifier).toInt() else -> DEFAULT_PREFETCH_CACHE } } private val networkState = NetworkState() private val networkCallback = object : ConnectivityManager.NetworkCallback() { // Network capabilities have changed for the network override fun onCapabilitiesChanged( network: Network, networkCapabilities: NetworkCapabilities ) { networkState.setDefaultNetwork(network, networkCapabilities) } override fun onLost(network: Network?) { networkState.setDefaultNetwork(null, null) } } private val telephonyCallback = object : TelephonyCallback(), TelephonyCallback.PreciseDataConnectionStateListener { override fun onPreciseDataConnectionStateChanged(dataConnectionState: PreciseDataConnectionState) { networkState.cellNetworkType = dataConnectionState.networkType } } connectivityManager.registerDefaultNetworkCallback(networkCallback) telephonyManager.registerTelephonyCallback(telephonyCallback) private val prefetchCacheSize: Int get() { return when { hasWifi -> MAX_PREFETCH_CACHE hasCellular -> (DEFAULT_PREFETCH_CACHE * cellModifier).toInt() else -> DEFAULT_PREFETCH_CACHE } } }
Java
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); private boolean hasWifi = false; private boolean hasCellular = false; private float cellModifier = 1f; private ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() { @Override public void onCapabilitiesChanged( @NonNull Network network, @NonNull NetworkCapabilities networkCapabilities ) { super.onCapabilitiesChanged(network, networkCapabilities); hasCellular = networkCapabilities .hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR); hasWifi = networkCapabilities .hasTransport(NetworkCapabilities.TRANSPORT_WIFI); } }; private PhoneStateListener phoneStateListener = new PhoneStateListener() { @Override public void onPreciseDataConnectionStateChanged( @NonNull PreciseDataConnectionState dataConnectionState ) { switch (dataConnectionState.getNetworkType()) { case (TelephonyManager.NETWORK_TYPE_LTE | TelephonyManager.NETWORK_TYPE_HSPAP): cellModifier = 4; Break; case (TelephonyManager.NETWORK_TYPE_EDGE | TelephonyManager.NETWORK_TYPE_GPRS): cellModifier = 1/2.0f; Break; default: cellModifier = 1; Break; } } }; cm.registerDefaultNetworkCallback(networkCallback); tm.listen( phoneStateListener, PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE ); public int getPrefetchCacheSize() { if (hasWifi) { return MAX_PREFETCH_SIZE; } if (hasCellular) { return (int) (DEFAULT_PREFETCH_SIZE * cellModifier); } return DEFAULT_PREFETCH_SIZE; }
Cómo optimizar las solicitudes iniciadas por la app
Las solicitudes que inicia la app suelen ocurrir según un programa, como una app que envía registros o estadísticas a un servicio de backend. Cuando se trata de problemas iniciados por la app individuales, considera la prioridad de esas solicitudes, si pueden agruparse y si pueden aplazarse hasta que el dispositivo se esté cargando o Conectarse a una red no medida. Estas solicitudes se pueden optimizar con una programación cuidadosa y con el uso de bibliotecas como WorkManager.
Cómo agrupar solicitudes de red
En un dispositivo móvil, el proceso de activar la radio, establecer conexión y mantener la radio activa consume mucha batería. Por lo tanto, el procesamiento de solicitudes individuales en momentos aleatorios puede además reducir su duración. Para optimizar este proceso, puedes poner en cola una serie de solicitudes de red y procesarlas juntas. Esto permite que el sistema pague la energía el costo de encender la radio una sola vez y seguir recibiendo todos los datos que solicita una aplicación.
Usa WorkManager
Puedes usar la biblioteca WorkManager
para realizar trabajos de manera eficiente.
que considere si se cumplen condiciones específicas, como la disponibilidad de la red
y el estado de energía. Por ejemplo, supongamos que tienes una subclase Worker
llamada DownloadHeadlinesWorker
que recupera los titulares de noticias más recientes. Este trabajador
se puede programar para que se ejecute cada hora, siempre que el dispositivo esté conectado a una
la red no medida y la batería del dispositivo no está baja, con una estrategia de reintento personalizada
si hay algún problema con la recuperación de los datos, como se muestra a continuación:
Kotlin
val constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.UNMETERED) .setRequiresBatteryNotLow(true) .build() val request = PeriodicWorkRequestBuilder<DownloadHeadlinesWorker>(1, TimeUnit.HOURS) .setConstraints(constraints) .setBackoffCriteria(BackoffPolicy.LINEAR, 1L, TimeUnit.MINUTES) .build() WorkManager.getInstance(context).enqueue(request)
Java
Constraints constraints = new Constraints.Builder() .setRequiredNetworkType(NetworkType.UNMETERED) .setRequiresBatteryNotLow(true) .build(); WorkRequest request = new PeriodicWorkRequest.Builder(DownloadHeadlinesWorker.class, 1, TimeUnit.HOURS) .setBackoffCriteria(BackoffPolicy.LINEAR, 1L, TimeUnit.MINUTES) .build(); WorkManager.getInstance(this).enqueue(request);
Además de WorkManager, la plataforma de Android ofrece muchas otras herramientas. para ayudarte a crear un cronograma eficiente para completar tareas de creación de redes, como como sondeos. Para obtener más información sobre el uso de estas herramientas, consulta la Guía para el procesamiento en segundo plano.
Optimiza las solicitudes iniciadas por el servidor
Por lo general, las solicitudes que inicia el servidor se producen en respuesta a una notificación de un servidor. Por ejemplo, una app que se usa para leer los artículos de noticias más recientes puede recibir una notificación sobre un nuevo lote de artículos que se ajusten al preferencias de personalización, que luego descarga.
Envía actualizaciones del servidor con Firebase Cloud Messaging
Firebase Cloud Messaging (FCM) es un mecanismo ligero que se usa para transmitir datos desde un servidor a una instancia de app en particular. Con FCM, el servidor puede notificar a la app en ejecución en un dispositivo en particular que hay nuevos datos disponibles para ella.
En comparación con el sondeo, en el que la app debe hacer ping al servidor de forma regular para buscar datos nuevos, este modelo controlado por eventos permite que tu app cree una conexión nueva solo cuando sabe que hay datos para descargar. El modelo minimiza las capacidades y reduce la latencia cuando se actualiza información dentro de la app.
FCM se implementa utilizando una conexión TCP/IP persistente. Esto minimiza la la cantidad de conexiones persistentes y permite que la plataforma optimice el ancho de banda y minimizan el impacto asociado a la duración de la batería.