Restrições do sistema em tarefas em segundo plano

Os processos em segundo plano podem consumir muita memória e bateria. Por exemplo, uma transmissão implícita pode iniciar muitos processos em segundo plano que foram registrados para ouvi-la, mesmo que esses processos não tenham tanta utilidade. Isso pode ter um impacto significativo no desempenho do dispositivo e na experiência do usuário.

Para evitar restrições do sistema, use a API certa para sua tarefa em segundo plano. A documentação Visão geral das tarefas em segundo plano ajuda você a escolher a API certa para suas necessidades.

Restrições iniciadas pelo usuário

Se um app apresenta alguns dos maus comportamentos descritos no Android vitals, o sistema solicita que o usuário restrinja o acesso desse app aos recursos do sistema.

Se o sistema perceber que um app está consumindo recursos em excesso, ele vai notificar o usuário e dará a opção de restringir as ações do app. Os comportamentos que podem acionar o aviso incluem:

  1. Wake locks em excesso: um wake lock parcial mantido por uma hora quando a tela está desativada
  2. Serviços em segundo plano em excesso: o app segmenta níveis de API inferiores a 26 e há excesso de serviços em segundo plano.

As restrições precisas impostas são determinadas pelo fabricante do dispositivo. Por exemplo, em versões do AOSP, os apps restritos não podem executar tarefas, acionar alarmes nem usar a rede, exceto quando o app está no primeiro plano.

Restrições para o recebimento de transmissões de atividade de rede

Os apps não recebem CONNECTIVITY_ACTION transmissões quando se registram para recebê-las no manifesto. Processos que dependem dessa transmissão não serão iniciados. Isso pode representar um problema para apps que querem ouvir mudanças de rede ou realizar atividades de rede em massa quando o dispositivo se conecta a uma rede ilimitada. Várias soluções para contornar essa restrição já existem no framework do Android, mas escolher o caminho certo depende do que você quer que o app faça.

Agendar trabalho em conexões ilimitadas

Ao criar um WorkRequest, adicione uma 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)
}

Quando as condições para o trabalho forem atendidas, o app vai receber um callback para executar o doWork() método na classe Worker especificada.

Monitorar a conectividade de rede enquanto o app está em execução

Os apps em execução ainda podem detectar CONNECTIVITY_CHANGE com um BroadcastReceiverregistrado. No entanto, a ConnectivityManager API fornece um método mais robusto para solicitar um callback somente quando as condições de rede especificadas são atendidas.

NetworkRequest objetos definem os parâmetros do callback de rede em termos de NetworkCapabilities. Você cria NetworkRequest objetos com a classe NetworkRequest.Builder. registerNetworkCallback em seguida, transmite o objeto NetworkRequest para o sistema. Quando as condições da rede são atendidas, o app recebe um callback para executar o onAvailable() método definido na classe ConnectivityManager.NetworkCallback.

O app continua recebendo callbacks até o app sair ou chamar unregisterNetworkCallback().

Restrições para o recebimento de transmissões de imagens e vídeos

Os apps não podem enviar nem receber transmissões ACTION_NEW_PICTURE ou ACTION_NEW_VIDEO. Essa restrição ajuda a reduzir os impactos no desempenho e na experiência do usuário quando vários apps precisam ser ativados para processar uma nova imagem ou vídeo.

Determinar quais autoridades de conteúdo acionaram o trabalho

WorkerParameters permite que o app receba informações úteis sobre quais autoridades de conteúdo e URIs acionaram o trabalho:

List<Uri> getTriggeredContentUris()

Retorna uma lista de URIs que acionaram o trabalho. Essa lista fica vazia se nenhum URI tiver acionado o trabalho (por exemplo, se ele foi acionado devido a um prazo ou outro motivo) ou se o número de URIs alterados for maior que 50.

List<String> getTriggeredContentAuthorities()

Retorna uma lista de strings de autoridades de conteúdo que acionaram o trabalho. Se a lista retornada não estiver vazia, use getTriggeredContentUris() para recuperar os detalhes sobre quais URIs foram modificados.

O exemplo de código abaixo modifica o CoroutineWorker.doWork() método e registra as autoridades de conteúdo e os URIs que acionaram o job.

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()
    }
}

Testar o app em restrições do sistema

A otimização dos seus apps para serem executados em dispositivos com pouca memória ou em condições de pouca memória pode melhorar o desempenho e a experiência do usuário. A remoção de dependências em serviços em segundo plano e de broadcast receivers implícitos registrados pelo manifesto pode ajudar o app a funcionar melhor nesses dispositivos. Recomendamos que você otimize seu app para ser executado totalmente sem esses processos em segundo plano.

Alguns outros comandos do Android Debug Bridge (ADB) podem ajudar a testar o comportamento do app quando esses processos em segundo plano estão desativados:

  • Para simular condições em que transmissões implícitas e serviços em segundo plano estão indisponíveis, digite o comando abaixo:

    $ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND ignore

  • Para reativar transmissões implícitas e serviços em segundo plano, digite o comando abaixo:

    $ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND allow

Otimizar ainda mais seu app

Para outras boas maneiras de otimizar o comportamento das tarefas em segundo plano, consulte a documentação Otimizar o uso da bateria para APIs de agendamento de tarefas.