Processos e ciclo de vida do app

Na maioria dos casos, cada aplicativo para Android é executado no próprio processo do Linux. Esse processo é criado para o app quando parte do código precisa ser executada e permanecerá em execução até que não seja mais necessário e o sistema precise reivindicar a memória para uso por outros apps.

Uma característica incomum e fundamental do Android é que o ciclo de vida do processo de um app não é diretamente controlado pelo próprio app. Em vez disso, ele é determinado pelo sistema por meio de uma combinação das partes do app que o sistema sabe que estão sendo executadas, da importância delas para o usuário e da quantidade de memória disponível no sistema.

É importante que os desenvolvedores de apps entendam como componentes de aplicativo diferentes (em especial Activity, Service e BroadcastReceiver) afetam o ciclo de vida do processo do app. Não usar esses componentes corretamente pode fazer com que o sistema elimine o processo do app enquanto ele estiver fazendo um trabalho importante.

Um exemplo comum de um bug do ciclo de vida do processo é um BroadcastReceiver que inicia uma linha de execução quando recebe um intent no método BroadcastReceiver.onReceive() e depois retorna a partir da função. Uma vez retornado, o sistema considera que o BroadcastReceiver não está mais ativo e, assim, o processo de hospedagem dele não é mais necessário, a menos que outros componentes do aplicativo estejam ativos nele. Assim, o sistema pode eliminar o processo a qualquer momento para recuperar a memória e, ao fazer isso, encerrar a linha de execução gerada no processo. A solução para esse problema normalmente é programar um JobService a partir do BroadcastReceiver para que o sistema saiba que ainda há trabalho ativo sendo feito no processo.

Para determinar quais processos precisam ser eliminados quando a memória estiver baixa, o Android coloca cada processo em uma "hierarquia de importância" baseada nos componentes executados neles e no estado desses componentes. Esses tipos de processo são, em ordem de importância:

  1. Um processo em primeiro plano é aquele que é necessário para o que o usuário está fazendo no momento. Vários componentes de aplicativo podem fazer com que o processo que o contém seja considerado em primeiro plano de maneiras diferentes. Um processo é considerado em primeiro plano se atender alguma das seguintes condições:
  2. Só haverá alguns desses processos no sistema, e eles só serão eliminados como um último recurso se a memória estiver tão baixa que nem mesmo esses processos possam continuar em execução. Geralmente, nesse ponto, o dispositivo atingiu um estado de paginação de memória, então essa ação é necessária para manter a interface do usuário responsiva.

  3. Um processo visível está realizando uma tarefa da qual o usuário está ciente, então encerrá-lo causaria um impacto negativo significativo na experiência do usuário. Um processo é considerado visível nas seguintes condições:
    • Ele está executando um Activity visível para o usuário na tela, mas não em primeiro plano. O método onPause() foi chamado. Isso pode ocorrer, por exemplo, se a atividade em primeiro plano for exibida como uma caixa de diálogo que permite que a atividade anterior seja vista por trás dela.
    • Ele tem um Service em execução como um serviço em primeiro plano por meio de Service.startForeground(), que está pedindo que o sistema trate o serviço como algo de que o usuário está ciente ou que algo esteja essencialmente visível.
    • Ele hospeda um serviço que o sistema está usando para um recurso específico do qual o usuário está ciente, como um plano de fundo interativo, serviço de método de entrada etc.

    O número desses processos em execução no sistema é menos limitado que os processos em primeiro plano, mas ainda relativamente controlado. Esses processos são considerados extremamente importantes e não serão eliminados, a menos que isso seja necessário para manter todos os processos em primeiro plano em execução.

  4. Um processo de serviço é aquele que contém um Service que foi iniciado com o método startService(). Embora esses processos não estejam diretamente visíveis para o usuário, eles geralmente fazem coisas com as quais o usuário se importa, como upload ou download de dados de rede em segundo plano. Assim, o sistema sempre mantém esses processos em execução, a menos que não haja memória suficiente para reter todos os processos em primeiro plano e visíveis.

    Os serviços que estão em execução há muito tempo, como 30 minutos ou mais, podem ter a importância rebaixada para permitir que o processo seja enviado para a lista de LRUs em cache descrita a seguir. Isso ajuda a evitar situações em que serviços de longa duração com vazamentos de memória ou outros problemas consumam muita memória RAM, impedindo o sistema de fazer um uso eficiente de processos em cache.

  5. Um processo armazenado em cache é um que não é necessário no momento. Portanto, o sistema está livre para eliminá-lo conforme desejado quando a memória for necessária em outro lugar. Em um sistema com comportamento normal, esses são os únicos processos envolvidos no gerenciamento de memória: um sistema bem executado terá vários processos em cache sempre disponíveis, para alternar entre apps de modo mais eficiente, e eliminará com frequência os mais antigos, conforme necessário. Somente em situações muito críticas e indesejadas, o sistema chegará a um ponto em que todos os processos em cache serão eliminados e começará a eliminar processos de serviço.

    Esses processos geralmente contêm uma ou mais instâncias Activity que não estão visíveis no momento para o usuário. O método onStop() foi chamado e retornado. Desde que implementem o ciclo de vida da atividade corretamente (consulte Activity para ver mais detalhes), quando o sistema eliminar esses processos, a experiência do usuário não será afetada ao retornar para o app. Ele poderá restaurar o estado salvo anteriormente quando a atividade associada for recriada em um novo processo.

    Esses processos são mantidos em uma lista de pseudo-LRU, em que o último processo na lista é o primeiro a ser eliminado para recuperar a memória. A política exata de ordenação nessa lista é um detalhe de implementação da plataforma, mas geralmente tentará manter processos mais úteis (aquele que hospeda o app de início do usuário, a última atividade que ele viu etc.) antes de outros tipos de processos. Outras políticas para eliminar processos também podem ser aplicadas: limites rígidos no número de processos permitidos, limites no tempo que um processo pode permanecer continuamente armazenado em cache etc.

Ao decidir como classificar um processo, o sistema baseará a decisão no nível mais importante encontrado entre todos os componentes ativos no processo. Consulte a documentação Activity, Service e BroadcastReceiver para ver mais detalhes sobre como cada um desses componentes contribui para o ciclo de vida geral de um processo. A documentação de cada uma dessas classes descreve mais detalhadamente como elas afetam o ciclo de vida geral do app.

A prioridade de um processo também pode ser aumentada com base em outras dependências de um outro processo a ele. Por exemplo, se o processo A estiver vinculado a Service com a sinalização Context.BIND_AUTO_CREATE ou estiver usando um ContentProvider no processo B, a classificação do processo B será sempre ao menos tão importante quanto a do processo A.