O sistema Android tenta manter um processo de candidatura pelo maior tempo possível, mas, eventualmente, precisa remover os processos antigos para recuperar a memória para novos processos ou mais importantes. Para determinar quais os processos manter e quais matar, o sistema coloca cada processo em uma "hierarquia de importância" com base nos componentes em execução no processo e o estado desses componentes. Processos com menor importância são eliminadas em primeiro lugar, em seguida, aqueles com a menor importância seguinte, e assim por diante, sempre que necessário para recuperar os recursos do sistema.
Há cinco níveis na hierarquia de importância. A lista a seguir apresenta os diferentes tipos de processos em ordem de importância (o primeiro processo é o mais importante e é morto por último):
1. Processo em primeiro plano: Um processo que é necessário para que o usuário esteja fazendo atualmente. Um processo é considerado em primeiro plano, se qualquer uma das seguintes condições forem verdadeiras:
Abriga uma Activity com a qual o usuário está interagindo com (o método onResume() da Activity foi chamado).
Abriga um Service que está vinculado à atividade que o usuário está interagindo.
Abriga um Service que está sendo executado "em primeiro plano", o serviço chamado é startForeground().
Abriga um Service que está executando um ciclo de vida de seus retornos (onCreate(), onStart(), ou onDestroy()).
Abriga um BroadcastReceiver que está executando a sua onReceive(). Geralmente, apenas um processo de background existe em um determinado momento. Eles são mortos apenas como um último recurso - se a memória é tão
pouca que ele não pode continuar rodando. Geralmente, nesse ponto, o dispositivo atingiu um estado de paginação de memória, então, matar alguns processos de primeiro plano é necessário para manter a interface de usuário sensível.
2. Processo Visível
Um processo que não tem nenhum componente de primeiro plano, mas ainda pode afetar o que o usuário vê na tela. Um processo é considerado visível se qualquer uma das seguintes condições forem verdadeiras:
Abriga uma Activity que não está em primeiro plano, mas ainda é visível para o usuário (seu método onPause() foi chamado). Isso pode ocorrer, por exemplo, se a atividade iniciou um plano de diálogo, que permite que a atividade anterior possa ser vista por trás dele.
Abriga um Service que está vinculado a uma atividade (ou plano) visível. Um processo visível é considerado extremamente importante e não vai ser morto a menos que isso é necessário para manter todos os processos de primeiro plano em execução.
3. Processo de serviço
Um processo que está executando um serviço que foi iniciado com o método
startService() e não se enquadra em nenhuma das duas categorias acima. Embora os processos de serviço não estejam diretamente ligados a qualquer coisa que o usuário vê, eles estão geralmente fazendo coisas que o usuário se preocupa (como a reprodução de música no fundo ou baixando arquivo na rede), então o sistema os mantém rodando, a menos que não haja memória suficiente para retê- los, juntamente com o primeiro plano e processos visíveis.
4. Processo em segundo plano
Um processo que mantém uma atividade que não é visível para o usuário no momento (o método onStop() da atividade foi chamado). Esses processos não têm impacto direto sobre a experiência do usuário, e o sistema pode matá-los a qualquer momento para recuperar a memória para primeiro plano, visibilidade, ou processo de serviço. Normalmente, há muitos processos em execução no
fundo, então eles são mantidos em uma LRU (menos recentemente usada), lista para garantir que o processo com a atividade que mais recentemente foi visto pelo usuário é o último a ser morto. Se uma atividade implementa métodos de seu ciclo de vida corretamente, e salva o seu estado atual, matar o seu processo não terá um efeito visível sobre a experiência do usuário, pois quando o usuário navega de volta para a atividade, a atividade restaura todo o seu estado visível. 5. Processo vazio
Um processo que não possui todos os componentes do aplicativo ativos. A única razão para manter esse tipo de processo vivo é para fins de armazenamento em cache, para melhorar o tempo de inicialização da próxima vez que um componente precisa ser executado. O sistema mata muitas vezes estes processos a fim de equilibrar os recursos do sistema global entre os caches do processo e os cachês do kernel subjacente.
Android enfileira um processo ao mais alto nível que pode, com base na importância do componente ativo no processo. Por exemplo, se um processo hospeda um serviço e uma atividade visível, o processo é classificado como um processo visível, e não um processo de serviço.
Além disso, o ranking de um processo pode ser acrescido, pois outros processos são dependentes dele - um processo que está servindo a outro processo nunca pode ser menor classificado do que o processo que está servindo. Por exemplo, se um provedor de conteúdo em um processo A está a servir um cliente no processo de B, ou se um serviço em um processo A está ligado a um componente no processo de B, processo A é sempre considerado pelo menos tão importante como o processo B.
Porque um processo executando um serviço é melhor classificado do que um processo com atividades em segundo plano, uma atividade que inicia uma operação de longa duração pode muito bem começar um serviço para essa operação, ao invés de simplesmente criar uma thread de trabalhador - particularmente se a operação provavelmente durará mais que a atividade. Por exemplo, uma atividade que carrega uma foto em um site deve iniciar um serviço para realizar o upload, então o upload pode continuar em segundo plano, mesmo se o usuário deixar a atividade. Usar um serviço garante que a operação terá, pelo menos, prioridade "no processo do serviço",
independentemente do que acontece com a atividade. Este é o mesmo motivo que os receptores de broadcast devem contratar serviços ao invés de simplesmente colocar operações demoradas em um thread.
Thread
Quando uma aplicação é lançada, o sistema cria um thread de execução para o aplicativo, chamado de "main". Esta thread é muito importante, pois é responsável por despachar os eventos para os apropriados widgets de interface de usuário, incluindo eventos de desenho. É também o segmento em que sua aplicação interage com os componentes da UI Toolkit Android (componentes da android.widget e pacotes android.view). Como tal, o thread principal é chamado também às vezes de UI thread. O sistema não cria um thread separado para cada instância de um componente. Todos os componentes que são executados no mesmo processo são instanciados no thread de interface do usuário e as chamadas do sistema para cada componente são despachadas a partir desse thread. Conseqüentemente, os métodos que respondem às callbacks do sistema (como onKeyDown() para relatar as ações do usuário ou um método de retorno do ciclo de vida) sempre executam no thread da interface do usuário do processo.
Por exemplo, quando o usuário toca um botão na tela, seu thread UI do aplicativo despacha o evento de toque para o widget, que por sua vez, define seu estado pressionado e lança uma requisição inválida para a fila de eventos. O thread UI desenfilera a requisição e notifica o widget que deve se redesenhar.
Quando o aplicativo executa um trabalho intensivo em resposta à interação do usuário, este único thread pode produzir mal desempenho a menos que você implemente a sua aplicação de forma adequada. Especificamente, se tudo está acontecendo no thread de interface do usuário, realizando longas operações, tais como acesso à rede ou consultas de banco de dados, irá bloquear a interface toda. Quando o thread está bloqueado, nenhum evento pode ser enviado, incluindo eventos de desenho. Do ponto de vista do usuário, o aplicativo parece travar. Pior ainda, se o segmento de interface do usuário estiver bloqueado por mais de alguns segundos (cerca de 5 segundos atualmente) ao usuário é apresentado o abominável "aplicativo não responde" (ANR). O usuário pode, então, decidir abandonar a aplicação e desinstalá-la se está infeliz.
Além disso, o Andoid UI Toolkit não é thread-safe. Então, você não deve manipular a sua interface a partir de um thread de trabalho - você deve fazer toda a manipulação de sua interface a partir de um thread UI. Assim, existem apenas duas regras para o modelo de thread única no Android:
1. Não bloquear o thread de UI
2. Não acessar o Android UI Toolkit de fora do thread UI
Threads funcionais
Devido ao modelo de thread único acima descrito, é vital para a capacidade de resposta da interface do usuário do seu aplicativo que você não bloqueie o thread. Se você tiver de realizar operações que não são instantâneas, você deve certificar-se de fazê-las em threads separados ("background" ou threads "worker").
Por exemplo, abaixo está um código para um usuário que faz o download de uma imagem de um thread separado e é exibida em uma ImageView:
publicvoid onClick(View v){
newThread(newRunnable(){
publicvoid run(){
Bitmap b = loadImageFromNetwork("http://example.com/image.png");
mImageView.setImageBitmap(b);
}
}).start(); }
Em princípio, isso parece funcionar bem, porque cria uma nova thread para lidar com a operação da rede. No entanto, ele viola a segunda regra do modelo de única thread: não
acessar o Android UI Toolkit de fora da UI-thread - esta amostra modifica o
ImageView do thread de trabalho em vez do thread UI. Isso pode resultar em um comportamento indefinido e inesperado, que pode ser difícil e demorado para rastrear. Para corrigir esse problema, o Android oferece várias maneiras para acessar a thread de interface do usuário a partir de outros threads. Aqui está uma lista de métodos que podem ajudar:
Activity.runOnUiThread(Runnable) View.post(Runnable)
Por exemplo, você pode corrigir o código acima usando o método View.post(Runnable):
publicvoid onClick(View v){
newThread(newRunnable(){
publicvoid run(){
finalBitmap bitmap = loadImageFromNetwork("http://example.com/image.png");
mImageView.post(newRunnable(){
publicvoid run(){
mImageView.setImageBitmap(bitmap);
}
});
}
}).start(); }
Agora, essa implementação é thread-safe: o funcionamento da rede é feito a partir de um segmento separado, enquanto o ImageView é manipulado a partir do thread de interface de usuário.
No entanto, como a complexidade da operação cresce, este tipo de código pode ser complicado e difícil de manter. Para lidar com interações mais complexas com um thread de trabalho, você pode considerar usar um Handler, para processar as mensagens entregues a partir do thread. Talvez a melhor solução, porém, é estender a classe AsyncTask, o que simplifica a execução de tarefas do thread de trabalho que precisam interagir com a interface do usuário.