Ver mensagens sem resposta | Ver tópicos ativos Hoje é 23 Mai 2013, 16:52



Responder Tópico  [ 6 Mensagens ] 
 [Tutorial] Android, processamento concorrente, threads 
Autor Mensagem
Dalvik Virtual Machine
Dalvik Virtual Machine

Data de registro: 17 Jul 2011, 10:55
Mensagens: 1995
Localização: São Paulo
Mensagem [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()&gt;=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
Perfil
Dalvik Virtual Machine
Dalvik Virtual Machine
Avatar de usuário

Data de registro: 05 Abr 2011, 03:47
Mensagens: 1846
Localização: Rio de Janeiro
Mensagem 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 @bemobi
Motorola 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
Perfil WWW
Dalvik Virtual Machine
Dalvik Virtual Machine

Data de registro: 17 Jul 2011, 10:55
Mensagens: 1995
Localização: São Paulo
Mensagem 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
Perfil
Dalvik Virtual Machine
Dalvik Virtual Machine
Avatar de usuário

Data de registro: 05 Abr 2011, 03:47
Mensagens: 1846
Localização: Rio de Janeiro
Mensagem 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 @bemobi
Motorola 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
Perfil WWW
Dalvik Virtual Machine
Dalvik Virtual Machine

Data de registro: 17 Jul 2011, 10:55
Mensagens: 1995
Localização: São Paulo
Mensagem 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
Perfil
Anatomy of an App
Anatomy of an App

Data de registro: 25 Nov 2011, 15:17
Mensagens: 158
Localização: Florianópolis
Mensagem Re: [Tutorial] Android, processamento concorrente, threads
Muito Legal!

_________________
--
Att,
Leandro


05 Mar 2012, 16:23
Perfil
Mostrar mensagens anteriores:  Organizar por  
Responder Tópico   [ 6 Mensagens ] 

Quem está online

Usuários vendo este fórum: adenilsonsena, AdrianoMM, akaytatsu, alfredo_ej, allart, alylson, am2net, Andreid, Aniquiler, antoniodourado, apoena, ARAKINIDIO, ariostorecco, arkanjo, arnaldo.miranda, Ask Jeeves, attomweb, Berbert, bernardodauer, BiG_SerGiO, bigr ecreio, bocajunior88, BOMBER27, BornSlip, boydivalor, brcamp, brenno, brian15, bruno.abd, Bruto_JPO, btminzon, buccalon, Caique, careisjr, cavenaghi, cfranca, Chewbacca, Cleiton, Comandante Cody, Cr-Informática, criscmaia, culumin, dannieltec, dentimskol, diemesleno, dnakamashi, dr.faro, Dudi_FC, edervieira, edsonel, EduardoYC, eduzortea, edwarvelarde, emersonbarros, erikopa, estratec, Everton Moreira, felipedsilva, felipetesc, ferrodecaju, Francisco_Geraldo, freak, frederico.affini, freina, genildof, gfgodoy, Google Android, guilhermesmo, HanNiBaLSeTDf, hellbest, Heritrix, hopper.frf, ice, inaciofernandes, inesfg, italoraony, ixian, jacquesbica, jairodealmeida, jasn, JeffCF, jefficojava, jorgecardoso, jpespindola, JRSIQUEIRA, klausenner, konos, lafamac, leanderdulac, leonardodamata, lillian.brandao, Lindberg, Lord, luciocamilo, Lucious, luizcarlosvb, luizneto79, Luke Skywalker, maiconjunches, Marcelo, marcelorferrari, marcosf63, mvoto, n3t0, neiesc, neviim, oliverlessa, persiomotta, Petto, phfmendes, piagg, pilon, pomarolli, Portal Android, pumadeejay, pxcx, Qui-Gon Jinn, rafael.winter, ramonrabello, renatodondoni, rfrafa, ricardoogliari, ricdigital, rodrigo aguiar, rodrigosalfer, ronanPlus, rosano, rotilho, schiroky, ScoobyGB, Shmi Skywalker, siker C3PO, SirBagda, srmoreira, suportecr, tassiovirginio, Telekom [Bot], Thelemita, Thiago, thiago20, thiagoalgo, tiagocomerio, Tognoli, vicfalmac, viniciusluiz, wingdoido, Wookiees, Yuri, zorba e 7 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

Procurar por:

© 2007 - 2013 Portal Android - Comunidade de Desenvolvedores da Plataforma Android

Estamos no Linkedin    Siga-nos no twitter


Powered by phpBB - Hospedado por Bemobi