Neste tutorial, vamos explicar o como o Android atua ao executar um serviço, descreveremos em que consistem os threads de execução e sobre o que tratam os processos. Isso nos permitirá entender a forma como nossos aplicativos são executados, dando-nos maior controle e estabilidade nos dispositivos móveis onde serão instalados.
Fio
Quando o usuário executa um aplicativo, O Android cria um thread chamado principal (principal). Este thread é muito importante porque se encarrega de gerenciar os eventos que o usuário aciona para os componentes apropriados e também inclui os eventos que desenham a tela. Um thread de execução, a menor parte que pode ser processada por um planejador em um sistema operacional, neste caso o Android (com kernel Linux).
O implementação de vários threads que são processados ao mesmo tempo no mesmo aplicativo (chame-o de simultaneidade, que se refere à simultaneidade de execução), é conhecido como multithreading. Multithreading é aplicado para que esses threads compartilhem recursos E é isso que um processo compreende, lembre-se de que isso pode ser aplicado programaticamente dentro do código do mesmo aplicativo, a implementação de multithreading no nível do sistema operacional não depende de nós.
O início do ciclo de vida de um aplicativo inclui a geração de um novo processo Linux ao qual é atribuído um thread principal ou UI thread (o thread responsável pelo processamento gráfico do aplicativo, thread da interface do usuário em inglês).
ObservaçãoO ciclo de vida inclui a execução dos métodos: onCreate (), onStart () e onResume (); no início e no encerramento: onPause (), onStop () e onDestroy ().
Um processo pode ser forçado a fechar pelo Android por falta de memória, esse tipo de caso é raro devido ao avanço tecnológico, mas ainda acontece.
A pergunta é: Quais processos o Android decide fechar?
Estas são fechadas comparando seu nível de importância, é resumido da seguinte forma:
O mais importante: processos de primeiro planoO usuário está interagindo com o referido processo (o método onResume () do referido processo está em execução). Existe um serviço que está executando seus métodos de ciclo de vida. Ou existe um Receptor de radiodifusão executando o dele método onReceive ().
O segundo mais importante: processos visíveisAtividade com chamada para método onPause (). Serviço vinculado a uma atividade visível (serviço vinculado).
O terceiro mais importante: Processo com um serviçoO usuário não está interagindo diretamente com o processo. O processo tem um serviço em execução em segundo plano.
O segundo menos importante: processo em segundo planoNão existe nenhum tipo de interação com o usuário. O processo visualizado mais recentemente pelo usuário será o último a ser destruído.
O menos importante: Processo vazioNão possui componentes ativos. O processo ainda está ativo para fins de cache, evitando que o usuário volte a utilizá-lo.
Este último, o processo vazio, é o primeiro a ser encerrado em caso de falta de memória. Assim, um aplicativo que implementa um serviço onde é criado um thread para baixar conteúdo da internet, será mais importante do que um aplicativo que crie o thread sem implementar um serviço, de modo que é mais provável que seja encerrado antes de concluir o download. , porque são processos de longa duração.
Para entender o multhreading, vamos ver como o Android lida com seu thread principal.
PROCESS A tem uma IU ou Tópico PRINCIPAL, este tópico trata de um fila de mensagens ou fila de mensagens, que funciona quando o thread fica ocioso, quem lida com isso? O Looper.
Looper é uma classe de interface do usuário de Android Java que, junto com o Classe Handler, processa eventos da interface do usuário, como botões pressionados, telas redesenhadas e interruptores de orientação. Os eventos também podem ser usados para carregar conteúdo em um serviço HTTP, redimensionar imagens e executar solicitações remotas. O principal recurso dessas classes é que elas podem implementar um padrão de simultaneidade.
O Classe Android Looper contém um MessageQueue (fila de mensagens) e está associado apenas ao tópico a partir do qual foi criado. Observe que esta conexão não pode ser interrompida e que o lLooper não pode ser anexado a qualquer outro segmento. Além disso, o Looper está no armazenamento local e só pode ser chamado a partir de um método estático. Um método de teste verifica se um Looper já está associado a um thread e, em seguida, o método estático cria o Looper. Posteriormente, um loop pode ser usado para verificar as mensagens na fila.
Até agora, entendemos vários conceitos: processo, thread, thread de IU, looper, mas ainda não sabemos por que o multithreading.
Operações de longo prazo
É considerado de longa duração qualquer método cuja execução exceda 5 segundos, o que dispara a típica mensagem “a aplicação não responde. Você quer fechá-lo?
O que podem ser essas operações?: Acesso à Internet, Consultas SQL, análise XML / HTML / JSON, processamento gráfico complexo. Qualquer uma dessas operações que são executadas no thread principal irá bloqueá-lo e, como é a que lida com a interface gráfica do usuário, é interpretado como um congelamento, que o android decide fechar.
Vamos apenas imaginar que qualquer uma dessas operações dure 7 segundos e o usuário decida escrever algo em alguma entrada de texto, então enquanto esses 7 segundos não se passaram, o thread de IU não pode atualizar a visualização para que o usuário aprecie que ele está escrevendo, e para que ele gere um congelamento, a mensagem "sem resposta" é acionada com a qual você tem duas opções, esperar ou destruir, embora você nunca possa saber quanto tempo esperar, pode ser alguns segundos ou até minutos dependendo da fila de mensagens que têm o tópico principal.
Como evitamos o congelamento?
Usando threads ou serviços, dependendo se a tarefa requer a modificação da visualização, neste caso, um serviço é implementado porque a visualização de um aplicativo não pode ser modificada fora do thread de interface do usuário. A melhor maneira de evitar o congelamento é usar tarefas assíncronas com a classe AsyncTask. Neste tutorial, implementaremos vários threads para entender o comportamento da arquitetura Android.
Código e desenvolvimento
O projeto que iremos criar a seguir será baseado em um download de imagem com o qual devemos criar um tópico que nos permita gerenciar o acesso e download pela internet porque o A PRINCIPAL ou UI Thread não permite esta ação.
Começaremos criando um novo projeto com uma atividade vazia, denominamos este projeto "MultiThreadExample", com uma única atividade simples vamos criar a estrutura do arquivo XML que pertence a esta atividade.
Temos um campo de texto, um botão, um layout Linear que corresponde a uma barra de carregamento indeterminada que usaremos posteriormente e uma visualização em lista que contém uma matriz de URLs de imagens hospedadas na internet. No arquivo que contém a classe Java para nossa atividade (exclusiva), ela é escrita com o seguinte código:
package com.omglabs.multithreaexample; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.ProgressBar; public class MainActivity estende AppCompatActivity implementa AdapterView.OnItemClickListener {private EditText editText; ListView listView privado; urls String [] privadas; ProgressBar privado progressBar; private LinearLayout progressLayout; @Override protected void onCreate (Bundle savedInstanceState) {super.onCreate (savedInstanceState); setContentView (R.layout.activity_main); editText = (EditText) findViewById (R.id.downloadURL); listView = (ListView) findViewById (R.id.listurls); listView.setOnItemClickListener (this); urls = getResources (). getStringArray (R.array.URLs); progressBar = (ProgressBar) findViewById (R.id.progressbar); progressLayout = (LinearLayout) findViewById (R.id.progresslayout); } public void download (View view) {} @Override public void onItemClick (AdapterView adapterView, View view, int i, long l) {editText.setText (urls [i]); }}Até agora a aplicação pode ser compilada sem nenhum problema, nesta classe, declaramos as variáveis:
- editar texto
- exibição de lista
- urls
- Barra de progresso
- progressLayout
Um campo de texto, uma lista, um arranjo de string, uma barra de progresso e um Layout Linear.
No método onCreate Atribuímos a estes a respectiva vista que lhes pertence e que foi criada no arquivo XML da atividade, com exceção dos urls que atribuem seus valores da pasta de valores no arquivo de string e cuja disposição é declarada do seguinte modo:
http://www.fmdos.cl/wp-content/uploads/2016/03/1.jpg.webp http://vignette3.wikia.nocookie.net/teenwolf/images/9/90/Crystal_Reed_003.jpeg.webp https: // pbs.twimg.com/profile_images/699667844129107968/EvhTFBHN.jpg.webp http://vignette1.wikia.nocookie.net/teen-wolf-pack/images/0/0b/Holland-holland-roden-31699868-500-600.png.webpO download do método vazio (visualização View) será preenchido com o código que fará o download e que está vinculado ao Botão de download do bot por meio do atributo onclick. Finalmente, o método onitemclick que pertence a exibição de lista, ele preenche o campo de texto quando você clica em qualquer um dos urls contidos na lista. Depois de compilado, esse código terá a seguinte aparência:
Na próxima etapa iremos criar os métodos que farão o download, seguindo estas etapas:
- Crie um objeto da classe URL (java.net) que representará a url a ser baixada.
- Abra a conexão usando esse objeto.
- Leia os dados (via web) usando a classe de fluxo de entrada em uma matriz de bytes.
- Abra / crie um arquivo de fluxo de saída onde os dados de urls serão salvos no cartão SD.
- Grave os dados nesse arquivo.
- E finalmente feche a conexão.
Por enquanto, será assim:
download booleano público usandoThreads (link de string) {confirmação booleana = false; URL downloadLink = null; HttpURLConnection conne = null; InputStream inputStream = null; tente {downloadLink = novo URL (link); conexão = (HttpURLConnection) downloadLink.openConnection (); inputStream = conne.getInputStream (); } catch (MalformedURLException e) {e.printStackTrace (); } catch (IOException e) {e.printStackTrace (); } finalmente {if (connex! = null) {connex.disconnect (); } if (inputStream! = null) {tente {inputStream.close (); } catch (IOException e) {e.printStackTrace (); }}} confirmação de retorno; }Este método que construímos precisará apenas de um Corda que será o URL para download, é boleano Para confirmar o download, downloadLink é o objeto URL, conexão é a conexão que será feita para acessar o objeto e inputStream é aquele que fará a leitura dos dados, se tentarmos usar este método no botão downloadBot o aplicativo pararia devido a não poder ser executado no discussão principal.
Aqui vamos nós com o uso de threads, existem duas maneiras de fazer isso com uma classe e é estendendo essa classe para Thread ou implementando a classe Runnable, esta classe não é uma thread, ela simplesmente permite que você crie um método que você pode ser executado em um momento específico e, se você criar um thread separado, execute-o nele.
Dentro do botão de download, escreveremos este código, e ele terá a seguinte aparência:
download público void (visualização da vista) {Thread mThread = new Thread (new mRunn ()); mThread.start (); }Aqui estamos criando um novo thread que precisa de um objeto Runnable que criamos em uma classe privada como esta:
classe privada mRunn implementa Runnable {@Override public void run () {download usingThreads (urls [0]); }}Criar aula particular
ObservaçãoLembre-se de que tudo isso está na classe Java de nossa única atividade.
Com a linha:
baixar usandoThreads (urls [0]);Estamos chamando a função que criamos onde abrimos a conexão, um item do array URL é passado a ela para que possa ler os dados daquele endereço. Mais tarde, ele será modificado.
Se tentássemos executar este aplicativo pressionando o botão, o aplicativo pararia, pois precisamos de uma permissão especial para acessar a internet, que é solicitada através do manifesto de nosso aplicativo. Adicionando a linha, antes do rótulo:
Agora, para verificar se o aplicativo realmente executa o download, adicionaremos algumas linhas de código ao método de download usingThreads, Isso parecerá assim:
download booleano público usingThreads (link String) {confirmação booleana = false; URL downloadLink = null; HttpURLConnection conne = null; InputStream inputStream = null; FileOutputStream archOutputStream = null; Arquivo arquivo = nulo; tente {downloadLink = novo URL (link); conexão = (HttpURLConnection) downloadLink.openConnection (); inputStream = conne.getInputStream (); arquivo = novo Arquivo (Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS) + "/" + Uri.parse (link) .getLastPathSegment ()); archOutputStream = novo FileOutputStream (arquivo); leitura int = -1; byte [] buffer = novo byte [1024]; while ((Read = inputStream.read (buffer))! = -1) {archOutputStream.write (buffer, 0, Read); } confirmação = verdadeiro; } catch (MalformedURLException e) {e.printStackTrace (); } catch (IOException e) {e.printStackTrace (); } finalmente {if (connex! = null) {connex.disconnect (); } if (inputStream! = null) {tente {inputStream.close (); } catch (IOException e) {e.printStackTrace (); }} if (archOutputStream! = null) {tente {archOutputStream.close (); } catch (IOException e) {e.printStackTrace (); }}} confirmação de retorno; } FileOutputStream archOutputStream = null; Arquivo arquivo = nulo;As declarações destes objetos representam a escrita do arquivo que está sendo lido, e o arquivo vazio onde a leitura será salva.
arquivo = novo Arquivo (Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS) + "/" + Uri.parse (urls [0]). getLastPathSegment ()); archOutputStream = novo FileOutputStream (arquivo); int Read = -1; byte [] buffer = novo byte [1024]; while ((Read = inputStream.read (buffer))! = -1) {archOutputStream.write (buffer, 0, Read); } confirmação = verdadeiro;"Arquivo" é o objeto Arquivo vazio cujo endereço é construído acessando o cartão SD "Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS)" e adicionando uma barra "/" e o último segmento da URL que geralmente representa o nome do arquivo para download, conseguimos isso com o método getLastPathSegment ().
Antes de testar o aplicativo, adicionaremos uma última permissão no manifesto:
Depois de executar a aplicação no emulador ou dispositivo Android, ao premir o botão veremos que aparentemente nada acontece, mas se verificarmos a pasta Download com um explorador de ficheiros perceberemos que o primeiro item da lista foi descarregado; uma foto chamada 1.jpg.webp.
Para fazer isso aplicativo dinâmico e implementar o URL do listview, vamos atualizar o método de download (visualização da vista) e vamos adicionar isso, como a primeira linha:
String link = editText.getText (). ToString ();E no classe mRunn vamos adicionar isso, antes do método run ():
classe privada mRunn implementa Runnable {link String privado; mRunn público (link de string) {this.link = link; } @Override public void run () {baixar usandoThreads (link); }}E no classe mRunn vamos adicionar isso, antes do método run ():
Assim, podemos passar a variável de link do campo de texto para o método que realiza o download. A aplicação neste ponto está totalmente funcional, embora falte um pouco de facilidade de uso, então vamos tentar corrigir isso, usando a barra de progresso que declaramos no início.
Na classe mRunn no método run (), incluiremos:
MainActivity.this.runOnUiThread (new Runnable () {@Override public void run () {progressLayout.setVisibility (View.VISIBLE);}});Antes da chamada para o downloadusandoThreads. Isso fará com que a barra de carregamento apareça quando pressionamos o botão, na cláusula finally do método de download usingThreads.
Vamos adicionar:
this.runOnUiThread (new Runnable () {@Override public void run () {progressLayout.setVisibility (View.GONE);}});Assim, quando o download for concluído, a barra desaparecerá novamente. Isso ocorrerá independentemente de o download ser bem-sucedido ou não.
E isso foi tudo, um breve implementação de vários tópicosIsso é um pouco tedioso e traz algumas complicações para aplicativos mais complexos.A maneira mais eficaz de realizar essa tarefa, no nosso caso, baixando algumas imagens, é usando o AsyncTasks.