Restrições do sistema para trabalho 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 possam não fazer muito trabalho. 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 de tarefas em segundo plano ajuda você a escolher a API certa para suas necessidades.

Restrições iniciadas pelo usuário

Se um app exibe 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 excessivos, ele vai notificar o usuário e oferecer 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á desligada.
  2. Serviços em segundo plano excessivos: se o app for direcionado a níveis de API anteriores ao 26 e tiver excesso de serviços em segundo plano

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

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

Os apps não recebem transmissões CONNECTIVITY_ACTION quando se registram para recebê-las no manifesto. Além disso, os processos que dependem dessa transmissão não são iniciados. Isso pode representar um problema para apps que querem detectar 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 a correta depende do que você quer que o app realize.

Programar trabalho em conexões ilimitadas

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

Quando as condições do trabalho forem atendidas, o app receberá um callback para executar o método doWork() 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 BroadcastReceiver registrado. No entanto, a API ConnectivityManager fornece um método mais robusto para solicitar um callback somente quando as condições de rede especificadas são atendidas.

Os objetos NetworkRequest definem os parâmetros do callback de rede em termos de NetworkCapabilities. Crie objetos NetworkRequest com a classe NetworkRequest.Builder. registerNetworkCallback transmite o objeto NetworkRequest para o sistema. Quando as condições de rede são atendidas, o app recebe um callback para executar o método onAvailable() 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 aliviar 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. Esse parâmetro ficará vazio se nenhum URI tiver acionado o trabalho (por exemplo, se o trabalho tiver sido 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 a seguir modifica o método CoroutineWorker.doWork() 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 app com restrições do sistema

A otimização dos apps para execução em dispositivos com pouca memória ou em condições com 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. É recomendável otimizar a execução do app sem usar esses processos em segundo plano.

Alguns comandos extras do Android Debug Bridge (adb) podem ajudar a testar o comportamento do app com esses processos em segundo plano desativados:

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

    $ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND ignore

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

    $ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND allow

Otimizar ainda mais seu app

Para conhecer 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.