Visão geral dos processos e threads

Quando um componente do aplicativo é iniciado e não tem outros componentes em execução, o sistema Android inicia um novo processo Linux para o aplicativo com um único thread de execução. Por padrão, todos os componentes do mesmo aplicativo são executados no mesmo processo e linha de execução, chamada 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, e o componente começa nesse processo e usa a mesma linha de execução. No entanto, é possível organizar diferentes componentes em seu aplicativo para serem executados em processos separados, e é possível criar linhas de execução 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 mude isso. No entanto, se você achar que precisa controlar qual processo uma determinada ao qual o componente pertence, faça isso no arquivo de manifesto.

A entrada de manifesto para cada tipo de elemento componente (<activity>, <service>, <receiver> e <provider>) é compatível com um atributo android:process que pode especificar uma 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.

Você também pode definir android:process para que os componentes de aplicativos diferentes sejam executados na mesma desde que os aplicativos compartilhem o mesmo ID de usuário do Linux e sejam assinados com o os mesmos certificados.

O <application> também é compatível com um atributo android:process, que você pode usar 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 são exigidos por outros processos que atendem o usuário de maneira mais imediata. Aplicação os componentes em execução no processo que é encerrado são, consequentemente, destruídos. Um processo é iniciado novamente para esses componentes quando há trabalho para eles fazerem.

Ao decidir quais processos encerrar, o sistema Android avalia a importância relativa deles para o usuário. Por exemplo, ele encerra mais facilmente 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. A decisão de encerrar um processo, portanto, 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 abordados 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. Esse thread é muito importante, pois é responsável por despachar eventos para os widgets apropriados da interface do usuário, incluindo eventos de desenho. Também é quase sempre a linha de execução em que o aplicativo interage com os componentes das APIs android.widget e do kit de ferramentas de interface do Android android.view pacotes. Por esse motivo, a linha de execução principal às vezes é chamada de linha de execução de interface. No entanto, em circunstâncias especiais, pode não ser a linha de execução de interface dela. Para mais informações, consulte Thread anotações.

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

Por exemplo, quando o usuário toca em um botão na tela, o thread de IU do aplicativo despacha o no widget, que por sua vez define seu estado pressionado e publica uma solicitação de invalidação para na fila de eventos. A linha de execução de IU retira a solicitação da fila e notifica o widget para que seja desenhado de novo por conta própria.

A menos que você implemente seu aplicativo corretamente, esse modelo podem ter um desempenho ruim quando o app realiza um trabalho intenso em resposta à interação do usuário. A execução de operações longas na linha de execução de IU, como acesso à rede ou consultas ao banco de dados, bloqueia toda a interface. Quando a linha de execução é bloqueada, nenhum evento pode ser despachado. incluindo eventos de desenho.

Da perspectiva do usuário, o aplicativo travará. Pior ainda, se a linha de execução de IU estiver bloqueada por mais de alguns segundos, o usuário recebe a mensagem "O aplicativo não respondendo" Caixa de diálogo (ANR). O usuário pode decidir sair do aplicativo ou até mesmo desinstalar reimplantá-lo.

O kit de ferramentas de interface do Android não é seguro para linhas de execução. Portanto, não manipule a interface em uma linha de execução de worker. Faça toda a manipulação da interface do usuário na interface fio Há duas regras para o modelo de linha de execução única do Android:

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

Threads de trabalho

Por causa desse modelo de thread único, é fundamental para a capacidade de resposta interface do aplicativo que não bloqueie o thread de IU. Se você tiver operações para realizar que não são instantâneos, faça isso em segundo plano ou linhas de execução worker. Lembre-se de que não é possível atualizar a IU por qualquer linha de execução que não seja na 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 de outros fios 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 interface.

No entanto, à medida que a complexidade da operação aumenta, esse tipo de código pode se tornar complicado e difíceis de manter. Para processar interações mais complexas com uma linha de execução de worker, considere usar usando 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 programar o trabalho em linhas de execução em segundo plano e se comunicar com a linha de execução de UI, 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 em mais de uma linha de execução. portanto, precisa ser gravado para ser thread-safe.

Isso é válido principalmente para métodos que podem ser chamados remotamente, como os métodos em um serviço vinculado. Quando uma chamada em um implementado em uma IBinder tem origem no mesmo processo em que o IBinder estiver em execução, o método será executado na linha de execução do autor da chamada. No entanto, quando a chamada tem origem em outro processo, o método é executado em uma linha de execução escolhida um pool 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 interface do processo.

Por exemplo, enquanto a infraestrutura O método onBind() é chamado na linha de execução de interface do processo do serviço, métodos implementados no objeto que onBind() retorna, como um subclasse que implementa métodos de chamada de procedimento remoto (RPC), são chamadas a partir de threads na piscina. Como um serviço pode ter mais de um cliente, é possível engajar mais de uma linha de execução no pool usando o mesmo método IBinder ao mesmo tempo. Portanto, os métodos IBinder precisam ser implementados para serem thread-safe.

Da mesma forma, um provedor de conteúdo pode receber solicitações de dados originadas em outros processos. ContentResolver e ContentProvider escondem 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 na interface para o processo. Como esses métodos podem ser chamados a partir de qualquer número de linhas de execução no ao mesmo tempo, eles também devem ser implementados para serem thread-safe.

Comunicação entre processos

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

Os valores de retorno são então são transmitidos na direção oposta. O Android fornece todo o código para executar essas IPCs para se concentrar em definir e implementar a interface de programação RPC.

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