|
Página 1 de 1
|
[ 6 Mensagens ] |
|
[Tutorial] Android, processamento concorrente, threads
| Autor |
Mensagem |
|
A H Gusukuma
Dalvik Virtual Machine
Data de registro: 17 Jul 2011, 10:55 Mensagens: 1990 Localização: São Paulo
|
 [Tutorial] Android, processamento concorrente, threads
O objetivo principal deste tutorial é mostrar um exemplo de processamento concorrente no Android, com compartilhamento simultâneo de dados. Para isso vamos usar threads, sincronização, variável volatile e interrupt. Aproveitando o teste, também usamos um ListAdapter customizado do BaseAdapter e as classes: Bundle, Message e Handler. Vamos inicialmente montar um projeto que consiste em utilizar um objeto para controlar um contador sequencial, uma thread para usar o contador e enviar mensagens com o último número gerado no contador para a activity, esta receberá as mensagens e mostrará os números em uma ListView. Vamos começar pelas telas. A tela main.xml, consiste em uma linha de status, dois botões de start e stop e a ListView que vai ser populada com o linhalista.xml que contém dois TextViews. main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:id="@+id/status" android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:text=""/>
<LinearLayout android:orientation="horizontal" android:id="@+id/linearLayout1" android:layout_width="fill_parent" android:layout_height="wrap_content"> <Button android:layout_width="wrap_content" android:id="@+id/start" android:layout_weight="1" android:text="Start" android:layout_height="wrap_content"></Button> <Button android:layout_width="wrap_content" android:id="@+id/stop" android:layout_weight="1" android:text="Stop" android:layout_height="wrap_content"></Button> </LinearLayout>
<View android:layout_height="2dip" android:layout_width="fill_parent" android:background="#ff909090" />
<ListView android:id="@+id/listView1" android:layout_height="0dip" android:layout_width="fill_parent" android:layout_weight="1"> </ListView> </LinearLayout>
linhalista.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content">
<TextView android:text="TextView" android:id="@+id/textView1" android:layout_weight="1" android:layout_width="0dip" android:layout_height="wrap_content"></TextView> <TextView android:text="TextView" android:id="@+id/textView2" android:layout_weight="1" android:layout_width="0dip" android:layout_height="wrap_content"></TextView> </LinearLayout>
Os dados que estamos tratando são o nome da Thread e o valor do contador. Usamos a classe Bundle para passar esses dois campos, e a classe Message para transferir o Bundle da thread para a activity via sendMessage da classe Handler. Na activity, o handler via handleMessage envia o Bundle para a lista e daí para a ListView. Segue o ListAdapter: MyListAdapter.java package br. com. agorandroid. testethread03; import java. util. LinkedList; import android. content. Context; import android. os. Bundle; import android. util. Log; import android. view. LayoutInflater; import android. view. View; import android. view. ViewGroup; import android. widget. BaseAdapter; import android. widget. TextView; public class MyListAdapter extends BaseAdapter { private LinkedList<Bundle> lista = new LinkedList<Bundle> (); private LayoutInflater inflater; public MyListAdapter (Context context, LinkedList<Bundle> lista ){ this. lista = lista; inflater = (LayoutInflater ) context. getSystemService(Context. LAYOUT_INFLATER_SERVICE); } @Override public int getCount () { return lista. size(); } @Override public Object getItem (int position ) { return lista. get(position ); } @Override public long getItemId (int position ) { return position; } @Override public View getView (int position, View convertView, ViewGroup parent ) { View view; if (convertView == null) { view = inflater. inflate(R. layout. linhalista, null); } else { view = convertView; } Bundle param = lista. get(position ); if (param == null) { Log. i("Teste", "erro param vazio"); } if (lista. size() > 0) { TextView tv1 = (TextView ) view. findViewById(R. id. textView1); tv1. setText(" "+ (int) param. getInt("contador", 0)); TextView tv2 = (TextView ) view. findViewById(R. id. textView2); tv2. setText((String) param. getString("nomeThread")); } return view; } } E o contador: ContadorSequencial.java package br.com.agorandroid.testethread03; /** * @author A H Gusukuma * agorandroid.blogspot.com */ public class ContadorSequencial { /* * Versão não synchronized */ private int contador; public ContadorSequencial() { contador = 0; } public int getContadorIncrementa() { return contador++; } public int getContador() { return contador; } }
Na thread, é importantíssimo notar que a variável workThread é definida como volatile. Ela deve ser definida dessa forma para garantir que ao ser alterada por outra thread ela seja visível para a thread com as alterações, caso contrário a thread pode não encerrar nunca! Segue a Thread: MyThread.java package br. com. agorandroid. testethread03; import android. os. Bundle; import android. os. Handler; import android. os. Message; /** * @author A H Gusukuma * agorandroid.blogspot.com */public class MyThread implements Runnable { private ContadorSequencial contador; private final Handler mHandler; private String nome; /** * workThread - thread que processa a contagem * usado também para sair do loop para encerrar a thread */ private volatile Thread workThread; /** * Construtor */ public MyThread (ContadorSequencial contador, Handler handler, String nome ) { this. mHandler = handler; this. contador = contador; this. nome = nome; this. workThread = new Thread(this, this. nome); } public void execute () { this. workThread. start(); } /* * */ @Override public void run () { while (this. workThread == Thread. currentThread()) { try { int cont = contador. getContadorIncrementa(); processaContador (cont ); Thread. sleep(200); } catch (InterruptedException e ) { } } } private void processaContador (int contador ) { Bundle param = new Bundle (); param. putString("nomeThread", this. nome); param. putInt("contador", contador ); Message msg = mHandler. obtainMessage(1, param ); mHandler. sendMessage(msg ); } public void shutDown () { Thread worker = this. workThread; if (worker != null) { this. workThread = null; worker. interrupt(); } }} Segue a activity: TesteThread03Activity.java package br. com. agorandroid. testethread03; import java. util. LinkedList; import android. app. Activity; import android. os. Bundle; import android. os. Handler; import android. os. Message; import android. view. View; import android. view. View. OnClickListener; import android. widget. Button; import android. widget. ListView; import android. widget. TextView; /** * @author A H Gusukuma * agorandroid.blogspot.com */public class TesteThread03Activity extends Activity { final private int MAX_LISTVIEW = 50; private MyListAdapter listAdapter; private ListView listView; private LinkedList<Bundle> filaUltimosNumeros = new LinkedList<Bundle> (); private MyThread mThread1; private ContadorSequencial count1; private TextView tv; private Handler mHandler = new Handler () { @Override public void handleMessage (Message msg ) { Bundle param = (Bundle ) msg. obj; atualizaLista (param ); } }; /** Called when the activity is first created. */ @Override public void onCreate (Bundle savedInstanceState ) { super. onCreate(savedInstanceState ); setContentView (R. layout. main); Button button = (Button)findViewById (R. id. start); button. setOnClickListener(mStartListener ); button = (Button)findViewById (R. id. stop); button. setOnClickListener(mStopListener ); tv = (TextView ) findViewById (R. id. status); listView = (ListView) findViewById (R. id. listView1); listAdapter = new MyListAdapter (this, filaUltimosNumeros ); listView. setAdapter(listAdapter ); count1 = new ContadorSequencial (); } private OnClickListener mStartListener = new OnClickListener () { public void onClick (View v ) { tv. setText("Contagem iniciada"); iniciarThreads (); } }; private OnClickListener mStopListener = new OnClickListener () { public void onClick (View v ) { pararThreads (); tv. setText("Contagem finalizada em "+count1. getContador()); } }; @Override public void onPause () { super. onPause(); pararThreads (); tv. setText(""); } private void iniciarThreads () { if (mThread1 == null) { mThread1 = new MyThread (count1, mHandler, "Thread_1"); mThread1. execute(); } } private void pararThreads () { if (mThread1 != null) { mThread1. shutDown(); mThread1 = null; } } private void atualizaLista (Bundle param ) { if (filaUltimosNumeros. size()>=MAX_LISTVIEW ) { filaUltimosNumeros. removeLast(); } filaUltimosNumeros. addFirst(param ); listAdapter. notifyDataSetChanged(); }} Nesse ponto, já podemos fazer um teste. Veremos na tela a ListView sendo populada por uma sequencia de números vindo da thread. Vamos fazer as seguintes alterações na activity: Acrescentar essas variáveis: private MyThread mThread2; private ContadorSequencial count2; private MyThread mThread3; private ContadorSequencial count3;
Incluir no final do onCreate: count2 = new ContadorSequencial(); count3 = new ContadorSequencial();
Incluir no iniciarThreads: if (mThread2 == null) { mThread2 = new MyThread(count2, mHandler, "Thread_2"); mThread2.execute(); } if (mThread3 == null) { mThread3 = new MyThread(count3, mHandler, "Thread_3"); mThread3.execute(); }
Incluir no pararThreads: if (mThread2 != null) { mThread2.shutDown(); mThread2 = null; } if (mThread3 != null) { mThread3.shutDown(); mThread3 = null; }
Faça um teste aqui. Vemos que cada thread está usando um contador separado, cada um com sua própria sequencia. E se pretendermos que seja uma contagem única? O quê precisamos fazer? Primeiramente, devemos alterar a classe do contador para permitir compartilhamento seguro entre Threads, colocando a palavra synchronized nos métodos que acessam a variável contador. Isso vai garantir que quando estivermos acessando a variável por uma thread, uma outra que tentar acessar vai esperar a outra liberar. Depois alterar a activity, e passar a mesma referência do objeto contador para as três threads. ContadorSequencial.java package br.com.agorandroid.testethread03; /** * @author A H Gusukuma * agorandroid.blogspot.com */ public class ContadorSequencial { /* * Versão synchronized */ private int contador; public ContadorSequencial() { contador = 0; } public synchronized int getContadorIncrementa() { return contador++; } public synchronized int getContador() { return contador; } }
O método iniciarThreads, fica assim: private void iniciarThreads() { if (mThread1 == null) { mThread1 = new MyThread(count1, mHandler, "Thread_1"); mThread1.execute(); } if (mThread2 == null) { mThread2 = new MyThread(count1, mHandler, "Thread_2"); mThread2.execute(); } if (mThread3 == null) { mThread3 = new MyThread(count1, mHandler, "Thread_3"); mThread3.execute(); } }
E testando, veremos uma sequencia única sendo enviados pelas três threads, compartilhando um único contador!
_________________Abraços ___________ Novo App: CalcMat - Calculadora de materiais para concreto Blog: Agorandroid - sobre programação Android Twitter: @Agorandroid ___________ Campanha: Facilite sua vida e a dos outros usuários Netiqueta:Procure responder as sugestões. Quando encontrar a solução, coloque [Resolvido] no primeiro post o mais rápido possível. Lembre-se de agradecer às pessoas que ajudaram e poste a solução do problema.
|
| 04 Mar 2012, 17:12 |
|
 |
|
antunes
Dalvik Virtual Machine
Data de registro: 05 Abr 2011, 03:47 Mensagens: 1846 Localização: Rio de Janeiro
|
 Re: [Tutorial] Android, processamento concorrente, threads
A H Gusukuma, Ótimo tutorial!! Está de parabéns pelos constantes tutoriais de alto nível!! Agora, aproveitando o assunto de thread, eu tenho uma dúvida, na verdade, um parêntese. Ouço vários programadores dizendo e já li em vários lugares que usar "interrupt" ou "stop" em threads não é seguro. Eu mesmo já tive problema em algumas threads quando as finalizava dessa forma. Nas minhas aplicações, sempre utilizo uma variável auxiliar para que as minhas threads terminem de maneira natural, ou seja, permitindo a saída de um "loop" infinito, e assim, terminando de executar todo o método "run()". Por exemplo: public class MinhaThread extends Thread { private boolean fim = false; public void finalizar () { this. fim = true; } public void run () { while (!fim ) { // conteúdo a ser executado } }} Como sei que vc conhece bem sobre threads, queria uma opinião sua. Como funciona esse tipo de coisa, é seguro ou não usar esses métodos?? Abraços!
_________________Alexandre Antunes @a3b @bemobiMotorola Defy App: IpCalc : Source - Calculadora IP (Máscara, Gateway, Broadcast e Rede). APP: Guia Carioca - Guia para o turista que visita o Rio de Janeiro e para o carioca que quer conhecer mais sua cidade. App: MyPonto - Controle de frequência (ponto) pessoal. Campanha: Facilite sua vida e a dos outros usuários. Objetivo: Vamos colocar a tag [RESOLVIDO] no título do tópico quando o problema for resolvido.Use a ferramenta de busca do fórum e não espere tanto para que a sua dúvida seja respondida!Quer trabalhar comigo??
|
| 04 Mar 2012, 18:15 |
|
 |
|
A H Gusukuma
Dalvik Virtual Machine
Data de registro: 17 Jul 2011, 10:55 Mensagens: 1990 Localização: São Paulo
|
 Re: [Tutorial] Android, processamento concorrente, threads
Obrigado, antunes. Na verdade o stop está banido, não apenas deprecated! O interrupt é utilizado para interromper um sleep, por exemplo. Ele por si só não encerra a thread. O teste seguinte é que vai interromper, já que antes a variável que controla o loop foi setado para null. É obrigatório usar pelo menos volatile na variável que controla o loop. Se não usar volatile, tem que usar synchronized no acesso à variável. A forma como você colocou no exemplo pode ficar num loop infinito pois o compilador pode otimizar o código e colocar o while (!fim) numa parte da memória (registradores) e a alteração do método finalizar feito por outra thread nunca ficar visível para a sua thread. Verifica que na rotina de shutDown, eu uso um artifício de salvar a referencia da thread numa variável local e depois setar a variável volatile que controla o fim do loop com null. Veja esse link, o autor é especialista em Java. http://www.ibm.com/developerworks/java/ ... index.html
_________________Abraços ___________ Novo App: CalcMat - Calculadora de materiais para concreto Blog: Agorandroid - sobre programação Android Twitter: @Agorandroid ___________ Campanha: Facilite sua vida e a dos outros usuários Netiqueta:Procure responder as sugestões. Quando encontrar a solução, coloque [Resolvido] no primeiro post o mais rápido possível. Lembre-se de agradecer às pessoas que ajudaram e poste a solução do problema.
|
| 04 Mar 2012, 18:31 |
|
 |
|
antunes
Dalvik Virtual Machine
Data de registro: 05 Abr 2011, 03:47 Mensagens: 1846 Localização: Rio de Janeiro
|
 Re: [Tutorial] Android, processamento concorrente, threads
A H Gusukuma,
Muito obrigado pela explicação!!
Dei uma lida em alguns materiais e entendi o que você fez, mas ainda não está suficientemente claro na minha cabeça. Garanto que lerei mais sobre o assunto.
Também não conhecia o modificador volatile, achei bem interessante o funcionamento dele. Acho que não tinha escutado falar dele pq ele tem uma função bem específica.
Já adianto pro pessoal que não é nada trivial, mas também não é impossível de se aprender!!
Os dias passam, lemos todos os dias, o dia inteiro e a gente descobre que não sabe quase nada. Brabo, né??
Vou até dar uma revisada no funcionamento das minhas threads e aproveitar pra aprender mais sobre o assunto!!
Abraços!!
_________________Alexandre Antunes @a3b @bemobiMotorola Defy App: IpCalc : Source - Calculadora IP (Máscara, Gateway, Broadcast e Rede). APP: Guia Carioca - Guia para o turista que visita o Rio de Janeiro e para o carioca que quer conhecer mais sua cidade. App: MyPonto - Controle de frequência (ponto) pessoal. Campanha: Facilite sua vida e a dos outros usuários. Objetivo: Vamos colocar a tag [RESOLVIDO] no título do tópico quando o problema for resolvido.Use a ferramenta de busca do fórum e não espere tanto para que a sua dúvida seja respondida!Quer trabalhar comigo??
|
| 04 Mar 2012, 22:29 |
|
 |
|
A H Gusukuma
Dalvik Virtual Machine
Data de registro: 17 Jul 2011, 10:55 Mensagens: 1990 Localização: São Paulo
|
 Re: [Tutorial] Android, processamento concorrente, threads
Antunes, O seu exemplo é bem mais intuitivo de entender, é só colocar o volatile na variável.
A forma que eu coloquei, não é tão perceptivel. Mas é muito utilizada, por isso eu coloquei assim. Se for ler códigos de outros programadores, é bem provavel de encontrar algo assim.
Agora, quanto a aprender algo novo, não tem como não ser assim. O Java é muito extenso.´ Eu tento compensar o fato de não saber muito, lendo o máximo que eu posso!
Eu uso volatile praticamente só nesse tipo de situação, como chave de dois estados. Mas é muito melhor do que usar uma variável com synchronized por ser mais leve num loop.
_________________Abraços ___________ Novo App: CalcMat - Calculadora de materiais para concreto Blog: Agorandroid - sobre programação Android Twitter: @Agorandroid ___________ Campanha: Facilite sua vida e a dos outros usuários Netiqueta:Procure responder as sugestões. Quando encontrar a solução, coloque [Resolvido] no primeiro post o mais rápido possível. Lembre-se de agradecer às pessoas que ajudaram e poste a solução do problema.
|
| 05 Mar 2012, 08:35 |
|
 |
|
lmc275
Anatomy of an App
Data de registro: 25 Nov 2011, 15:17 Mensagens: 158 Localização: Florianópolis
|
 Re: [Tutorial] Android, processamento concorrente, threads
Muito Legal!
_________________ -- Att, Leandro
|
| 05 Mar 2012, 16:23 |
|
|
|
Página 1 de 1
|
[ 6 Mensagens ] |
|
Quem está online |
Usuários vendo este fórum: Abraao, alexandre, Alexandre de Queiroz, alexcost2002, alexvbh, Andre Brito, andrigo, AT-AT, avsouza, bbourbon, betofigu, Bitetti, bruno.abd, brunogh, C0GuM3I0, caiodemarco, Cajux, camilodev, canaville, cariabs8, cehills, Chanceler Supremo Finis Valorum, chunga, ciro, Cr-Informática, criscmaia, dannieltec, Demerval, drjunior, dudu795, ederson_4, edson dias de gusmao, edsonel, elisa, eloilton, emersonbarros, EvertonLB, eXagon, fabiano_eletro, fabielp, Faroli, fcoroa, FelipeOliveira, flashking, fmakula, fracon, freak, fredjpa, freina, furlanrapha, Gabriel Teófilo, glmsistemas, Google Desktop, Gooooogle, grandebaro, Guilherme, guilhermepilotti, guilhermesmo, hebert, henrique.garcia, hugomarinho, interservic, ismavolk, JackBlackJack, jacquesbica, Jaison, jaydson, jcorreajr, jijo, JMurray, jpespindola, julianafsa, juliaojunior, Julio Assis, Juliobcosta, JuniorE, kennedyximenes, Ki-Adi-Mundi, klassmann, klausenner, kleberperea, lanlan, Lelinho, LForce, lkunta, loferreira, Lord, luizcesar, Luna, maolveira, Marcelo Lima, mateusff, MauNunes, Mayara Trevisol, MBetioli, Michel, Microdesk, n3t0, neiesc, nilsgome, nirvana, obitow, PAMinhoto, Paulo, paulo.esantos, paulo.weber, paulovaz, peterson.bah, phfmendes, phpower1, phsantos, Princesa Léia Organa, rafaelvital, Ranieri, raragao, rfrafael, Ricardo Chikasawa, Robson Florentino, rogerio.alcantara, rosano, rubens_olv, Samantha, sephct, sidnei.gs, silvio.carlos, slackware, taluna, tiagofalcao, tirloni, unnamedd, valaszek, weverton, williamcmello, wilsond, WiseNut e 6 visitantes |
|
Você não pode criar novos tópicos neste fórum Você não pode responder tópicos neste fórum Você não pode editar suas mensagens neste fórum Você não pode excluir suas mensagens neste fórum Você não pode enviar anexos neste fórum
|
|