Visão geral dos processos e threads

Quando um componente de aplicativo é iniciado e o aplicativo não tem nenhum outro componente em execução, o sistema Android inicia um novo processo do Linux para o aplicativo com uma única linha de execução. Por padrão, todos os componentes do mesmo aplicativo são executados no mesmo processo e linha de execução, chamados de linha de execução main.

Se um componente de aplicativo for iniciado e já houver um processo para esse aplicativo, porque outro componente do aplicativo já foi iniciado, o componente vai começar dentro desse processo e usar a mesma linha de execução de execução. No entanto, você pode organizar diferentes componentes no aplicativo para serem executados em processos separados e criar linhas de execução extras para qualquer processo.

Este documento aborda como os processos e os threads funcionam em um aplicativo Android.

Processos

Por padrão, todos os componentes de um aplicativo são executados no mesmo processo, e a maioria dos aplicativos não muda isso. No entanto, se você achar que precisa controlar a qual processo um determinado componente pertence, é possível fazer isso no arquivo de manifesto.

A entrada de manifesto para cada tipo de elemento de componente (<activity>, <service>, <receiver> e <provider>) oferece suporte a um atributo android:process que pode especificar um processo em que o componente é executado. É possível definir esse atributo para que cada componente seja executado no próprio processo ou para que alguns componentes compartilhem um processo, enquanto outros não.

Também é possível definir android:process para que componentes de aplicativos diferentes sejam executados no mesmo processo, desde que os aplicativos compartilhem o mesmo ID de usuário do Linux e sejam assinados com os mesmos certificados.

O elemento <application> também oferece suporte a um atributo android:process, que pode ser usado para definir um valor padrão que se aplica a todos os componentes.

O Android pode decidir encerrar um processo em algum momento, quando os recursos forem exigidos por outros processos que atendem o usuário de forma mais imediata. Consequentemente, os componentes do aplicativo em execução no processo que é encerrado são destruídos. Um processo é iniciado novamente para esses componentes quando há trabalho para eles.

Ao decidir quais processos encerrar, o sistema Android avalia a importância relativa dele para o usuário. Por exemplo, ele encerra mais prontamente um processo que hospeda atividades que não estão mais visíveis na tela, em comparação com um processo que hospeda atividades visíveis. Portanto, a decisão de encerrar um processo depende do estado dos componentes em execução nesse processo.

Os detalhes do ciclo de vida do processo e a relação dele com os estados do aplicativo são discutidos em Processos e ciclo de vida do app.

Linhas de execução

Quando um aplicativo é iniciado, o sistema cria uma linha de execução para ele, chamada de linha de execução principal. Essa linha de execução é muito importante, porque é responsável por enviar eventos para os widgets adequados da interface do usuário, incluindo eventos de desenho. Também é quase sempre a linha de execução em que o aplicativo interage com componentes dos pacotes android.widget e android.view do kit de ferramentas da interface do Android. Por esse motivo, a linha de execução principal às vezes é chamada de linha de execução de IU. No entanto, em circunstâncias especiais, a linha de execução principal de um app pode não ser a linha de execução de IU. Para ver mais informações, consulte Anotações de linha de execução.

O sistema não cria uma linha de execução separada para cada instância de um componente. Todos os componentes executados no mesmo processo são instanciados na linha de execução de IU, e as chamadas do sistema para cada componente são despachadas dessa linha de execução. Consequentemente, os métodos que respondem aos callbacks do sistema, como onKeyDown() para informar ações do usuário ou um método de callback do ciclo de vida, sempre são executados na linha de execução de interface do processo.

Por exemplo, quando o usuário toca em um botão na tela, a linha de execução de interface do app envia o evento de toque para o widget, que, por sua vez, define o estado pressionado e publica uma solicitação de invalidação na fila de eventos. A linha de execução de IU retira a solicitação da fila e notifica o widget para que ele seja redesenhado.

A menos que você implemente o aplicativo corretamente, esse modelo de linha de execução única pode produzir desempenho ruim quando o app realiza trabalhos intensos em resposta à interação do usuário. A realização de operações longas na linha de execução da interface, como acesso à rede ou consultas a banco de dados, bloqueia toda a interface. Quando a linha de execução é bloqueada, nenhum evento pode ser enviado, incluindo eventos de desenho.

Da perspectiva do usuário, o aplicativo parece travar. Pior ainda, se a linha de execução de IU for bloqueada por mais de alguns segundos, o usuário vai receber a caixa de diálogo "O app não está respondendo" (ANR). O usuário pode decidir sair do aplicativo ou até mesmo desinstalá-lo.

Lembre-se de que o kit de ferramentas de interface do Android não é seguro para linhas de execução. Portanto, não manipule a IU em uma linha de execução de worker. Faça toda a manipulação na interface do usuário na linha de execução de interface. Há duas regras para o modelo de thread único do Android:

  1. Não bloqueie a linha de execução de IU.
  2. Não acesse o kit de ferramentas de interface do Android de fora da linha de execução de interface.

Threads de trabalho

Devido a esse modelo de linha de execução única, é fundamental para a capacidade de resposta da interface do aplicativo que você não bloqueie a linha de execução de interface. Se você tiver operações para realizar que não sejam instantâneas, faça isso em linhas de execução de segundo plano ou worker separadas. Lembre-se de que não é possível atualizar a IU por nenhuma linha de execução além da linha de execução de IU, ou principal.

Para ajudar você a seguir essas regras, o Android oferece várias maneiras de acessar a linha de execução de IU em outras linhas de execução. Abaixo há uma lista dos métodos que podem ajudar você:

O exemplo a seguir usa View.post(Runnable):

Kotlin

fun onClick(v: View) {
    Thread(Runnable {
        // A potentially time consuming task.
        val bitmap = processBitMap("image.png")
        imageView.post {
            imageView.setImageBitmap(bitmap)
        }
    }).start()
}

Java

public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            // A potentially time consuming task.
            final Bitmap bitmap =
                    processBitMap("image.png");
            imageView.post(new Runnable() {
                public void run() {
                    imageView.setImageBitmap(bitmap);
                }
            });
        }
    }).start();
}

Essa implementação é segura para linhas de execução, porque a operação em segundo plano é feita em uma linha de execução separada, enquanto o ImageView é sempre manipulado na linha de execução de IU.

No entanto, à medida que a complexidade da operação aumenta, esse tipo de código pode ficar complicado e difícil de manter. Para processar interações mais complexas com uma linha de execução de worker, use um Handler na linha de execução de worker para processar mensagens entregues pela linha de execução de interface. Para uma explicação completa sobre como programar o trabalho em linhas de execução em segundo plano e se comunicar de volta para a linha de execução de IU, consulte Visão geral do trabalho em segundo plano.

Métodos seguros para threads

Em algumas situações, os métodos implementados são chamados a partir de mais de uma linha de execução e, portanto, precisam ser programados para serem seguros para linhas de execução.

Isso vale principalmente para métodos que podem ser chamados remotamente, como métodos em um serviço vinculado. Quando uma chamada em um método implementado em uma IBinder tem origem no mesmo processo em que o IBinder está sendo executado, o método é executado na linha de execução do autor da chamada. No entanto, quando a chamada se origina em outro processo, o método é executado em uma linha de execução escolhida em um conjunto de linhas de execução que o sistema mantém no mesmo processo que a IBinder. Ele não é executado na linha de execução de IU do processo.

Por exemplo, enquanto o método onBind() de um serviço é chamado na linha de execução de interface do processo do serviço, os métodos implementados no objeto retornado por onBind(), como uma subclasse que implementa métodos de chamada de procedimento remoto (RPC), são chamados a partir de linhas de execução no pool. Como um serviço pode ter mais de um cliente, mais de uma linha de execução de pool pode engajar o mesmo método IBinder ao mesmo tempo. Portanto, os métodos IBinder precisam ser implementados para serem seguros para linhas de execução.

Da mesma forma, um provedor de conteúdo pode receber solicitações de dados originadas em outros processos. As classes ContentResolver e ContentProvider ocultam os detalhes de como a comunicação entre processos (IPC) é gerenciada, mas os métodos ContentProvider que respondem a essas solicitações (os métodos query(), insert(), delete(), update() e getType()) são chamados de um pool de linhas de execução no processo do provedor de conteúdo, não da linha de execução de IU para o processo. Como esses métodos podem ser chamados a partir de qualquer número de linhas de execução ao mesmo tempo, eles também precisam ser implementados para serem seguros para linhas de execução.

Comunicação entre processos

O Android oferece um mecanismo para IPC usando RPCs, em que um método é chamado por uma atividade ou outro componente do aplicativo, mas executado remotamente em outro processo, com qualquer resultado retornado ao autor da chamada. Isso envolve a decomposição de uma chamada de método e os dados dela em um nível que o sistema operacional possa entender, transmitindo-a do processo local e do espaço de endereço para o processo remoto e o espaço de endereço e, em seguida, remontando e refazendo a chamada lá.

Os valores de retorno são transmitidos na direção oposta. O Android fornece todo o código para realizar essas transações de IPC para que você possa se concentrar em definir e implementar a interface de programação de RPC.

Para realizar a IPC, o aplicativo precisa se vincular a um serviço usando bindService(). Para mais informações, consulte a Visão geral dos serviços.