Restricciones del sistema cuando se trabaja en segundo plano

Los procesos en segundo plano pueden consumir mucha memoria y batería. Por ejemplo, una transmisión implícita puede iniciar muchos procesos en segundo plano registrados para escucharla, incluso si esos procesos no realizan mucho trabajo. Esto puede tener un impacto significativo en el rendimiento del dispositivo y la experiencia del usuario.

Si quieres evitar restricciones del sistema, asegúrate de usar la API correcta para la tarea en segundo plano. La documentación de Descripción general de las tareas en segundo plano te ayuda a elegir la API correcta según tus necesidades.

Restricciones iniciadas por el usuario

Si una app muestra algunos de los comportamientos inadecuados que se describen en Android vitals, el sistema le solicita al usuario que restrinja el acceso de esa app a los recursos del sistema.

Si el sistema nota que una app está consumiendo recursos excesivos, le notifica al usuario y le da la opción de restringir las acciones de la app. Entre los comportamientos que pueden activar la notificación, se incluyen los siguientes:

  1. Bloqueos de activación excesivos: 1 bloqueo de activación parcial retenido durante una hora cuando la pantalla está apagada.
  2. Servicios en segundo plano excesivos: Si la app se orienta a niveles de API inferiores a 26 y tiene demasiados servicios en segundo plano

Las restricciones precisas que se imponen son determinadas por el fabricante del dispositivo. Por ejemplo, en compilaciones de AOSP, las apps restringidas no pueden ejecutar trabajos, activar alarmas ni usar la red, excepto cuando están en primer plano.

Restricciones para la recepción de emisiones de actividad de red

Las apps no recibirán transmisiones de CONNECTIVITY_ACTION si se registran para recibirlas en su manifiesto, y no se iniciarán los procesos que dependan de esta transmisión. Esto podría ser un problema para las apps que deseen detectar los cambios en la red o realizar actividades de red masivas cuando se conecte el dispositivo a una red no medida. Ya existen varias soluciones para evitar esta restricción en el framework de Android, pero elegir la correcta depende de lo que quieras que logre tu app.

Cómo programar un trabajo en conexiones no medidas

Cuando compiles un WorkRequest, agrega un NetworkType.UNMETERED Constraint.

fun scheduleWork(context: Context) {
    val workManager = WorkManager.getInstance(context)
    val workRequest = OneTimeWorkRequestBuilder<MyWorker>()
       .setConstraints(
           Constraints.Builder()
               .setRequiredNetworkType(NetworkType.UNMETERED)
               .build()
           )
       .build()

    workManager.enqueue(workRequest)
}

Cuando se cumplan las condiciones para tu trabajo, tu app recibirá una devolución de llamada para ejecutar el método doWork() en la clase Worker especificada.

Cómo supervisar la conectividad de red mientras se ejecuta la app

Las apps que están en ejecución pueden detectar CONNECTIVITY_CHANGE con un BroadcastReceiver registrado. Sin embargo, la API de ConnectivityManager proporciona un método más sólido para solicitar una devolución de llamada solo cuando se cumplen las condiciones de red especificadas.

Los objetos NetworkRequest definen los parámetros de la devolución de llamada de red en términos de NetworkCapabilities. Puedes crear objetos NetworkRequest con la clase NetworkRequest.Builder. Luego, registerNetworkCallback pasa el objeto NetworkRequest al sistema. Cuando se cumplen las condiciones de red, la app recibe una devolución de llamada para ejecutar el método onAvailable() definido en su clase ConnectivityManager.NetworkCallback.

La app continúa recibiendo devoluciones de llamada hasta que se cierra o llama a unregisterNetworkCallback().

Restricciones para la recepción de emisiones de imagen y video

Las apps no pueden enviar ni recibir transmisiones ACTION_NEW_PICTURE ni ACTION_NEW_VIDEO. Esta restricción ayuda a aliviar el rendimiento y afecta la experiencia del usuario cuando se deben activar varias apps para procesar una imagen o un video nuevos.

Cómo determinar qué autoridades de contenido activaron un trabajo

WorkerParameters permite que tu app reciba información útil sobre qué autoridades de contenido y URIs activaron el trabajo:

List<Uri> getTriggeredContentUris()

Muestra una lista de los URI que activaron el trabajo. Estará vacío si ningún URI activó el trabajo (por ejemplo, se activó debido a una fecha límite o a otro motivo), o si el número de URIs modificados es mayor que 50.

List<String> getTriggeredContentAuthorities()

Muestra una lista de cadenas de autoridades de contenido que activaron el trabajo. Si la lista que se muestra no está vacía, usa getTriggeredContentUris() para recuperar los detalles de los URIs que se modificaron.

En el siguiente código de muestra, se anula el método CoroutineWorker.doWork() y se registran las autoridades de contenido y los URI que activaron el trabajo:

class MyWorker(
    appContext: Context,
    params: WorkerParameters
): CoroutineWorker(appContext, params)
    override suspend fun doWork(): Result {
        StringBuilder().apply {
            append("Media content has changed:\n")
            params.triggeredContentAuthorities
                .takeIf { it.isNotEmpty() }
                ?.let { authorities ->
                    append("Authorities: ${authorities.joinToString(", ")}\n")
                    append(params.triggeredContentUris.joinToString("\n"))
                } ?: append("(No content)")
            Log.i(TAG, toString())
        }
        return Result.success()
    }
}

Prueba la app en restricciones del sistema

Optimizar tus apps para que se ejecuten en dispositivos con poca memoria o en condiciones de poca memoria puede mejorar el rendimiento y la experiencia del usuario. Quitar las dependencias de los servicios en segundo plano y los receptores de emisión implícita registrados en manifiestos puede ayudar a que tu app se ejecute mejor en esos dispositivos. Te recomendamos que optimices tu app para que se ejecute sin usar por completo estos procesos en segundo plano.

Algunos comandos adicionales de Android Debug Bridge (ADB) pueden ayudarte a probar el comportamiento de la app con esos procesos en segundo plano inhabilitados:

  • Para simular condiciones en las que las emisiones implícitas y los servicios en segundo plano no están disponibles, ingresa el siguiente comando:

    $ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND ignore

  • Para volver a habilitar las emisiones implícitas y los servicios en segundo plano, ingresa el siguiente comando:

    $ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND allow

Cómo optimizar aún más tu app

Si deseas conocer otras formas útiles de optimizar el comportamiento de tus tareas en segundo plano, consulta la documentación Cómo optimizar el uso de la batería para las APIs de programación de tareas.