Restricciones del sistema en las tareas 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 que se registraron para recibirla, incluso si esos procesos no realizan mucho trabajo. Esto puede afectar de manera considerable tanto el rendimiento del dispositivo como la experiencia del usuario.

Para evitar las restricciones del sistema, asegúrate de usar la API correcta para tu tarea en segundo plano. La documentación de Descripción general de las tareas en segundo plano te ayuda a elegir la API adecuada para 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 envía una notificación 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 está orientada a niveles de API inferiores a 26 y tiene servicios en segundo plano excesivos.

Las restricciones precisas que se imponen son determinadas por el fabricante del dispositivo. Por ejemplo, en las compilaciones de AOSP, las apps restringidas no pueden ejecutar tareas, 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 reciben CONNECTIVITY_ACTION transmisiones 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 de red o realizar actividades de red masivas cuando se conecte el dispositivo a una red no medida. En el framework de Android, ya existen varias soluciones para evitar esta restricción, pero elegir la correcta depende del objetivo de tu app.

Cómo programar el trabajo en conexiones no medidas

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

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 el trabajo, la app recibirá una devolución de llamada a fin de ejecutar el doWork() método en la clase Worker especificada.

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

Las apps que se están ejecutando aú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 la red en términos de NetworkCapabilities. Puedes crear NetworkRequest objetos con la clase NetworkRequest.Builder. registerNetworkCallback luego pasa el objeto NetworkRequest al sistema. Cuando se cumplen las condiciones de red, la app recibe una devolución de llamada para ejecutar el onAvailable() método definido en su ConnectivityManager.NetworkCallback clase.

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 de ACTION_NEW_PICTURE o 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 nueva imagen o un nuevo video.

Cómo determinar qué autoridades de contenido activaron el 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 URIs que activaron el trabajo. Esta lista está vacía si ninguno de los URIs activó el trabajo (por ejemplo, se activó el trabajo 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 URIs 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()
    }
}

Cómo probar la app con 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. Si quitas las dependencias de los servicios en segundo plano y los receptores de transmisión implícita registrados en manifiestos, tu app funcionará mejor en esos dispositivos. Te recomendamos que optimices la app a fin de que se ejecute sin usar en absoluto 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 transmisiones 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 transmisiones 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

Para conocer otras formas de optimizar el comportamiento de tus tareas en segundo plano, consulta la documentación de Optimiza el uso de la batería para las APIs de programación de tareas.