Por que eu devo ler este artigo:

A comunicação Bluetooth é uma forma de comunicação sem fio muito utilizada entre dispositivos portáteis, seja pelo baixo consumo de energia ou pela grande variedade de dispositivos móveis que possuem este recurso. Para entender como funciona a comunicação bluetooth alguns conceitos são importantes, como a comunicação cliente-servidor, procura de servidores e serviços, troca de informações via fluxos (Stream), comunicação multithread, entre outros. No estudo de caso apresentado neste artigo muitos destes conceitos são apresentados, e ao final, o programa pode ser testado em diferentes aparelhos Android.

Assim, este artigo apresenta os fundamentos da comunicação bluetooth aplicados à plataforma Android. De maneira simples e didática, os fundamentos da programação bluetooth são apresentados ao leitor. Na sequência, um estudo de caso sobre a comunicação bluetooth na plataforma Android é apresentado, exemplificando conceitos como cliente-servidor, disponibilização de serviços, busca de servidores, conexão e envio e recepção de dados.


Guia do artigo:

Este artigo é útil para programadores que ainda não possuem conhecimentos técnicos sobre a comunicação bluetooth, e que desejam utilizar em seus aplicativos a troca de informações entre smartphones ou até mesmo entre smartphones e outros dispositivos portáteis bluetooth.

Bluetooth é uma tecnologia sem fio usada para conectar e transmitir dados entre dispositivos em rede pessoais (chamadas de Personal Area Networks - PANs). Com um alcance máximo de até 100m e velocidades de transmissão que podem se aproximar de 24 Mb/s, o bluetooth é um o padrão em comunicação de curta distância.

Celulares, smartphones, câmeras digitais, teclados, mouses e outros dispositivos adotaram a tecnologia por ser robusta, economizar energia e ter uma fácil implementação. Um dispositivo operando bluetooth pode se conectar com até oito outros dispositivos, que o torna uma ótima opção para uma rede de dispositivos móveis. O bluetooth surgiu como resposta para a necessidade de conectar dispositivos sem a utilização de cabos, visando a econômica de energia, a fácil operação e a comodidade.

Em 1994, a empresa Ericson iniciou um projeto para a eliminação dos cabos que conectavam os diversos periféricos dos telefones celulares. A ideia era utilizar ondas de rádio de baixa frequência para conectar os diferentes dispositivos.

A tecnologia originada pelo projeto da Ericson foi batizada de MCLink e surpreendeu por ser relativamente barata e de fácil implementação, o que contribuiu muito para o desenvolvimento do projeto que logo recebeu o apoio de outras empresas.

Mais tarde, a tecnologia recebeu o nome de bluetooth em homenagem ao rei dinamarquês Harold Blatand (em inglês - Harold Bluetooth), que unificou as tribos da Noruega, Suécia e Dinamarca. Foi utilizado o aspecto da unificação das nações para fazer uma referência a unificação dos dispositivos.

Em 1998 foi formado o consorcio BSIG (Bluetooth Special Interest Group) que iniciou as especificações industriais do bluetooth. A formação inicial do grupo pelas empresas Nokia, Ericson, IBM, Toshiba e Intel, facilitou ao projeto receber cada vez mais aceitação e apoio da comunidade tecnológica.

Funcionamento técnico do bluetooth

O bluetooth utiliza ondas de rádio de baixa potência, operando em frequências que vão de 2.4Ghz a 2.5 Ghz, na faixa de frequência conhecida como ISM (Industrial, Scientific, Medical). O uso da baixa potência aumenta a economia de energia das baterias e limita o alcance a um máximo de 100m.

A tecnologia é dividida em três classes levando em conta o alcance das ondas de rádio:

  • Classe 3 de 1 MW de potência: Alcança distâncias de até 1m;
  • Classe 2 de 10 MW potência: Alcança distâncias de até 10m;
  • Classe 1 de 100 MW potência: Alcança distâncias de até 100m.

Cada dispositivo possui uma classe de operação, que deve ser observado no momento de sua compra.

Existe também o conceito de Bluetooth Wireless Personal Area Network(BT-WPAN) que é o nome dado a área onde dispositivos bluetooth formam um rede. Uma BT-WPAN consiste de Piconets e Scatternets.

Uma piconet consiste de um conjunto de até oito dispositivos conectados, onde o dispositivo que iniciou a conexão é marcado como mestre e os demais como escravos. Duas piconets podem se conectar através de dispositivos comuns em ambas as redes, sendo a única restrição que estes dispositivos não sejam os mestres de suas piconets. Esta união de piconets recebe o nome de Scatternet, conforme exemplificado na Figura 1.

BT-WPAN
Figura 1. BT-WPAN

A tecnologia utiliza conexões ponto-multiponto onde o nó mestre é o controlador da rede. O nó mestre é o responsável por organizar a comunicação na piconet, sincronizando o clock entre os dispositivos conectados e calculando um padrão de frequency hopping. Os nós escravos se conectam somente com o nó mestre, não havendo conexão direta entre eles.

Outros dispositivos utilizam a mesma banda de frequência do bluetooth e para evitar interferência e garantir uma transmissão fim-a-fim segura, a tecnologia utiliza um mecanismo de alteração de frequência (frequency hopping). O mecanismo consiste em alterar a frequência de utilização em até 1600 vezes por segundo, gerando saltos na banda de frequência ISM. Os dispositivos sincronizam o padrão de saltos e se comunicam na mesma frequência.

As transmissões ocorrem no modo Full Duplex, onde os dispositivos transmitem e recebem dados por um esquema de divisão de tempo chamado TDD (Time Division Duplex).

Protocolos

A pilha de protocolos Bluetooth é dividida em três partes:

  • Camada de Transporte;
  • Camada Middleware;
  • Camada de Aplicação.

Os protocolos de transporte são responsáveis por localizar os dispositivos e gerenciar os links físicos e lógicos entre eles. Suportam tanto conexões síncronas quanto assíncronas e englobam as camadas de rádio frequência (RF), Baseband, Link Manager, Logical Link Control and Adaptation (L2CAP).

Os protocolos de Middleware são responsáveis por permitir a interação entre aplicações antigas e novas. Padrões como Point-toPoint Protocol (PPP), Wireless Application Protocol (WAP), Internet Protocol (IP),Trasmission Control Protocol (TCP) fazem parte desta camada.

A camada de aplicação faz referência aos aplicativos que podem usufruir da especificação bluetooth. A Figura 2 representa a pilha de protocolo bluetooth.

Pilha de protocolo Bluetooth
Figura 2. Pilha de protocolo Bluetooth

A seguir, uma rápida apresentação de cada sigla apresentada na figura:

  • RF ou RFCOM: Protocolo que habilita a comunicação serial através da emulação de portas na camada L2CAP;
  • Baseband: Controlador de Link - define conexão, endereçamento, formato dos pacotes, temporização e potência;
  • Link Manager: Define aspectos de segurança e gerencia os links entre os dispositivos;
  • Áudio: Para manter alta qualidade de serviço esperado para aplicações de áudio, o tráfego do mesmo é tratado como alta prioridade e é direcionado diretamente para a baseband, o qual é transmitido em pequenos pacotes diretamente para interface RF;
  • L2CAP: Responsável pela transparência de comunicação dos diferentes dispositivos;
  • TCP/IP: Protocolo já conhecido por muitos, pois é utilizado pela internet para a transmissão confiável de dados;
  • HID: Human Interface Device Profile é um perfil que define protocolos, procedimentos e características para utilizar dispositivos Bluetooth como teclados, controle de jogos ou dispositivos para monitoramento remoto;
  • RFCOM: Protocolo que habilita a comunicação serial através da emulação de portas na camada LLCAP;
  • Aplicação: A camada de aplicação consiste das próprias aplicações que utilizam links Bluetooth, estas podem incluir aplicações legadas ou aplicações orientadas a Bluetooth. Enquanto a maioria dos protocolos de rede fornece apenas canais entre entidades que se comunicam, deixando para os projetistas de aplicações a tarefa de descobrir a utilidade desses canais, a especificação Bluetooth identifica 13 aplicações específicas e fornece pilhas de protocolos para que os fabricantes usem os recursos Bluetooth de uma maneira compatível, garantindo assim a interoperabilidade entre os fabricantes.

Especificações

O BSIG é o responsável por padronizar as especificações bluetooth. Desde 1998 o grupo vem aperfeiçoando a tecnologia e lançando novos recursos com versões mais atuais.

A primeira versão do Bluetooth, a 1.0, foi publicada em junho de 1999 e tinha a capacidade de conectar somente um dispositivo por vez e uma taxa de transferência de 1Mbs compartilhada entre dados e voz. Meses depois foi lançada a versão 1.0b com algumas correções críticas.

A versão 1.1 foi a primeira a ser retificada como padrão IEEE recebendo a especificação número 802.15.1-2002, adicionou suporte a canais não encriptados e um indicador da força do sinal.

A especificação 1.2 foi a primeira a implementar o mecanismo de Frequency Hopping, além de aumentar as taxas de transferência de dados e adicionar alguns mecanismos para melhorar a qualidade no canal de voz. Foi retificada como o padrão IEEE 802.15.1-2005.

A especificação 2.0 implementou o EDR (Enhanced Data Rate), um mecanismo que possibilitou taxas de transferência de até 3 Mbs utilizando uma tecnologia de rádio aprimorada. A economia de energia passou a ser implementada também nas camadas de hardware dando uma melhor autonomia da bateria aos dispositivos.

A versão 2.1 possui mecanismos mais rápidos e confiáveis para auxiliar na descoberta de dispositivos operando bluetooth. Implementa também um mecanismo de economia de energia que reduz o consumo de energia quando o dispositivo está procurando por conexões ativas.

Foram adicionadas na versão 2.1 algumas especificações para segurança como: suporte ao EPR (Encryptation Puase Resume), SSP (Secure Simple Pairing) e NFC (Near Field Comunication). O EPR é uma ferramenta que possibilita utilizar uma chave de criptografia mutante, que aumenta a segurança em conexões que ficam ativas por um longo período (mais de 20h). O NFC cria automaticamente conexões seguras quando encontra dispositivos operando bluetooth + NFC.

A especificação 3.0 adiciona o uso de UWB (Ultra Wide-Band), que proporciona taxas de transferência que podem chegar a 54 Mb/s. Este mecanismo utiliza as especificações wireless IEEE 802.11 para atingir as altas taxas de transferência. Como o padrão 802.11 utiliza muita energia, esta ferramenta é ativada somente quando necessária, em transferências de muitos arquivos ou arquivos muito grandes.

A especificação 4.0 conta com inovações na economia de energia dos dispositivos. Foi adicionado uma estrutura de múltiplos perfis para os dispositivos, onde são agrupados conforme o consumo de energia. Dispositivos mais simples como relógios e fones de ouvido param de gastar energia assim que não estão mais sendo usados. Dispositivos como celulares e notebooks utilizam um perfil diferente pois suas conexões geralmente utilizam taxas de transferência maiores. O novo protocolo de segurança desta versão utiliza um algoritmo de criptografia de 128 bits.

Android e Bluetooth

A primeira versão do Android com suporte a bluetooth foi anunciada em 2009 pela Google com o nome de Eclair. A versão é a 2.0 e conta com suporte ao bluetooth 2.1. Desenvolvedores podem utilizar as classes nativas no Android e suas funções para criar aplicativos que utilizem o bluetooth.

Aplicações Android que tem suporte ao bluetooth podem descobrir e se conectar a dispositivos, estabelecer conexões ponto-a-ponto e ponto-multiponto e utilizar conexões RFCOMM para transferir dados.

Através de APIs (Interface de Programação de Aplicativos) os aplicativos desenvolvidos para Android podem configurar conexões, buscar, conectar e transferir dados entre os dispositivos.

Estas são as principais classes para se estabelecer conexões bluetooth no Android:

  • BluetoothAdapter: Representa o adaptador bluetooth local, o hardware de bluetooth. Utilizado para instanciar dispositivos bluetooth usando macs conhecidos e criar sokets bluetooth para receber conexões de outros dispositivos;
  • BluetoothDevice: Usado para requisitar informações e conexões a dispositivos remotos;
  • BluetoothSocket: Representa a interface da conexão;
  • BluetoothServeSocket: Habilita o servidor a receber pedidos de conexão;
  • BluetoothClass: Propriedades de somente leitura que definem as características e os serviços do dispositivo bluetooth;
  • BluetoothProfile: Representa um perfil Bluetooth. Existem vários perfis pré-definidos de configuração que podem ser escolhidos para se trabalhar como headset profile e o hands-free profile;
  • BluetoothHeadset: Suporte para headsets utilizando a tecnologia Bluetooth;
  • BluetoothA2p: Define a qualidade do áudio que pode ser transmitido em conexões entre dispositivos;
  • BluetoothProfile.ServiceListener: Avisa clientes bluetooth quando são desconectados ou conectados.

Das classes acima citadas, algumas serão utilizadas no estudo de caso apresentado a partir de agora.

Sistema proposto

Para apresentar na prática a comunicação bluetooth entre dispositivos móveis distintos, será utilizado um estudo de caso simples, onde os aparelhos trocam mensagens de texto. Para o desenvolvimento, foram utilizados dois aplicativos, o primeiro (aplicativo servidor) disponibilizará um serviço, onde esperará uma requisição (via texto) e responderá ao cliente (via texto). Já o segundo aplicativo (cliente) possuirá uma interface gráfica para pesquisa de servidor, onde serão apresentados todos os dispositivos móveis com bluetooth ligado próximos a ele. Após a seleção do servidor desejado, o aplicativo móvel enviará uma mensagem e aguardará a resposta, sendo esta apresentada na interface gráfica.

As Figuras 3, 4 e 5 apresenta a interface gráfica de ambos os dispositivos.

Interface do Aplicativo Servidor
Figura 3. Interface do Aplicativo Servidor
Interface do aplicativo cliente
Figura 4. Interface do aplicativo cliente
Interface do aplicativo cliente, após o conectar onde apresenta uma lista de dispositivos
Figura 5. Interface do aplicativo cliente, após o conectar onde apresenta uma lista de dispositivos

Desta forma, o usuário primeiro precisa iniciar o aplicativo servidor (deixando-o apto a receber mensagens Bluetooth), para na sequência iniciar o aplicativo cliente.

Apresentada a tela inicial do aplicativo cliente, deve-se clicar no botão conectar, o qual apresentará uma nova tela(lista) com todos os dispositivos bluetooth próximos. Nesta tela, o usuário pode clicar em Buscar, o qual inicia uma nova busca, ou selecionar o dispositivo no qual deseja se conectar (dispositivo servidor).

Selecionando o servidor, é apresentada novamente a tela inicial do aplicativo cliente, essa contendo o nome do servidor, assim, o usuário pode digitar uma mensagem e enviar ao servidor.

O servidor, por sua vez, receberá a mensagem, adicionará ela na tela, assim como o nome do cliente que a enviou, irá formatar uma nova mensagem de resposta e devolver ao cliente. Por fim, o aplicativo cliente apresentará na tela inicial do aplicativo a mensagem recebida.

Como observado, apesar de simples, este aplicativo apresenta todas as etapas para a troca de mensagens bluetooth. Após testar estas etapas, é possível aprimorar ainda mais o aplicativo, como o envio de múltiplas mensagens, dados binários (como imagens ou sons), entre outros.

Nota 1: Preparação do ambiente para o desenvolvimento Android.

Foge do escopo deste artigo apresentar a instalação e configuração do ambiente de desenvolvimento Android, cabendo ao leitor escolher qual a melhor plataforma para o desenvolvimento (Eclipse, Netbeans, Motodev, ADT Bundle, entre outros). Se você não tem conhecimento sobre como montar este ambiente de desenvolvimento, aconselha-se a leitura do artigo Configure um ambiente de desenvolvimento para Android da revista Mobile Magazine número 32 ou assista o curso Introdução ao Android SDK e o Curso de Android SDK – Dominando a API, do portal Devmedia.

Desenvolvimento do aplicativo servidor

Para o desenvolvimento do aplicativo servidor vamos criar um projeto Android. Como nome do projeto será utilizado ServidorBT. A Activity principal é chamada de ServidorBTActivity.java, já sua interface visual (arquivo XML) possui o nome de activity_servidor_bt.xml.

De forma resumida, um Activity nada mais é do que uma “atividade” do Android, ou seja, é um código fonte que pode ser executado (pode ser a primeira atividade executada quando se inicia o aplicativo ou pode ser executado a partir de outras telas, por exemplo).

A maioria das Activities estão associadas a interfaces visuais, sendo uma Activity (arquivo .java) definida para tratar uma interface gráfica (arquivo .xml). Nessa situação, é tarefa da Activity apresentar a tela definida no XML para o usuário, bem como tratar eventos e interações gerados na tela.

Para os testes, será utilizada a versão 2.1 do Android (Eclair), por esta possuir compatibilidade com a maioria dos smartphones (mesmo sendo mais antiga, esta versão já suporta perfeitamente a comunicação bluetooth).

Uma vez criado o projeto, o primeiro passo é desenvolver a interface visual, essa já apresentada na Figura 3. O código da interface é apresentado na Listagem 1.


  <LinearLayoutmlns:android=
  "http://schemas.android.com/apk/res/android"
    mlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
 
    <TetView
      android:id="@+id/tvEntrada"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:tet="Mensagem Recebida:"/>
  
    <TetView
       android:id="@+id/tvNomeCliente"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:tet="Nome do cliente:"/>
 
 </LinearLayout>
Listagem 1. activity_servidor_bt.xml – Interface gráfica do aplicativo servidor

Como a interface conta com apenas dois componentes visuais, os mesmos foram adicionados em um layout LinearLayout (linha 01), sendo este definido no formato vertical (linha 05). Desta forma, um componente será adicionado após o outro na interface.

Uma interface visual em Android possui dois grupos de componentes, os ViewGroups (componentes para gerenciamento de layout) e os View (componentes Visuais).

Dentre os principais ViewGroups existentes destacam-se o LinearLayout, o mais simples e provavelmente o mais utilizado em aplicações Android, onde os componentes são organizados um após o outro, entre suas variações estão a orientação vertical e horizontal. Também destaca-se o RelativeLayout, onde um componente é adicionado na tela usando como referência a posição do componente anterior, o TableLayout, o qual permite organizar os componentes no formato de tabela, semelhante ao que acontecia com o desenvolvimento das primeiras páginas html. Por fim, um layout, apesar de obsoleto ainda muito utilizado, o AbsoluteLayout, onde os componentes são inseridos em posições fixas na tela, que podem ser informadas em pixel, milímetro, polegadas, etc. Dada a riqueza visual da plataforma Android, existem outros gerenciadores de layout, com funções específicas e que podem ser utilizados, dependendo da necessidade do programador.

Já os View são componentes visuais, podendo estes ser componentes de entrada (como as caixas de texto e grupos de seleção), processamento ou interação (destacando-se neste grupo os vários tipos de botões e menus) e os componentes de saída (como textos estáticos e imagens). A plataforma Android possui também várias opções de componentes visuais, deixando a aplicação mais atrativa e amigável.

São utilizados dois componentes TextView na interface gráfica, o primeiro (linha 07) possui nome tvEntrada, sendo apresentada neste componente a mensagem recebida do aplicativo cliente. Já o segundo componente com nome tvNomeCliente (linha 13) apresentará o nome do dispositivo cliente que enviou a mensagem.

Desenvolvida a interface visual, o próximo passo é personalizar a classe ServidorBTActivity.java, iniciando com a importação das classes e declaração dos componentes necessários, conforme Listagem 2.


  import android.app.Activity;
  import android.content.Intent;
  import android.os.Bundle;
  import android.widget.TetView;
  import android.widget.Toast;
  import java.io.IOEception;
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.util.UUID;
  import android.bluetooth.BluetoothAdapter;
  import android.bluetooth.BluetoothServerSocket;
  import android.bluetooth.BluetoothSocket;
  
  public class ServidorBTActivity etends Activity {
  //Activity principal
      private TetView tvEntrada;
      private TetView tvNomeCliente;
  
      private BluetoothAdapter adapter; 
  
      private static final String NAME = "EccoServerBT";  
      private static final UUID MY_UUID = UUID.fromString(
          "fa87c0d0-afac-11de-8a39-0800200c9a66"); 
  
      @Override
      public void onCreate(Bundle savedInstanceState) { 
          super.onCreate(savedInstanceState);           
          setContentView(R.layout.activity_servidor_bt);
      }
  }//fim da classe ServidorBTActivity
Listagem 2. ServidorBTActivity.java – Importação das classes e declaração dos objetos utilizados no servidor

Das linhas 01 a 05 são importadas as classes referentes à estruturação de uma classe Android e também a interface gráfica. Das linhas 06 a 12 são importadas as classes para a utilização do bluetooth dentro do aplicativo.

A linha 14 declara a única Activity do aplicativo servidor, essa responsável pelo tratamento dos componentes visuais e a comunicação bluetooth. As linhas 15 e 16 declaram os dois componentes visuais da tela.

Já a linha 18 declara um objeto da classe BluetoothAdapter que representa o hardware bluetooth existente no dispositivo móvel. Como o aplicativo servidor fornece um “serviço” bluetooth (embora muito simples, o serviço oferecido recebe uma String do cliente, concatena a este uma mensagem e devolve ao cliente), este serviço precisa ter um nome e uma identificação única. O nome do serviço é definido na linha 20, já a identificação única (de 128 bits) é declarada como uma String de 32 caracteres hexadecimal – linha 22. O valor desta variável pode ser definido pelo usuário.

Por fim, o método onCreate (apresentado entre as linhas 25 e 28) precisa ser modificado sendo sua nova versão apresentada na Listagem 3.


   @Override
   public void onCreate(Bundle savedInstanceState) { 
       super.onCreate(savedInstanceState);             
       setContentView(R.layout.activity_servidor_bt);

       tvEntrada = (TetView) findViewById(R.id.tvEntrada);
       tvNomeCliente = (TetView) findViewById(R.id.tvNomeCliente);

       try{ 
        adapter = BluetoothAdapter.getDefaultAdapter(); 

        if(!adapter.isEnabled()){ 
          Intent enableIntent = new Intent(
          BluetoothAdapter.ACTION_REQUEST_ENABLE); 
          startActivityForResult(enableIntent, 2); 
        }
  
        new ConeaoThread(adapter).start();

      }catch(Eception e){
        Toast.makeTet( this, "Erro:" + e.getMessage(), 
         Toast.LENGTH_SHORT ).show();
       }
   }
} //fim da classe ServidorBTActivity
Listagem 3. ServidorBTActivity.java – Método onCreate

O método onCreate é responsável por formatar a tela inicial do aplicativo, bem como executar algumas operações iniciais do aplicativo. As linhas 03 e 04 iniciam a Activity e apresentam a tela inicial do aplicativo (activity_servidor_bt.xml). Já as linhas 06 e 07 recuperam os componentes visuais da tela.

A linha 09 inicia as operações para trabalhar com o bluetooth na plataforma Android. Esta linha é responsável por recuperar uma instância do adaptador bluetooth presente no dispositivo móvel, permitindo utilizar este para troca de mensagens. Após a recuperação, na linha seguinte (linha 12) é verificado se o bluetooth está ligado no dispositivo. Caso esteja desligado, via software é instanciado um objeto do tipo Intent, o qual permite interagir com recursos do dispositivo móvel, como ligar o bluetooth. Este procedimento é apresentado das linhas 13 a 15.

Caso ocorra um erro neste procedimento, uma exceção é gerada (linhas 21 e 22), apresentando uma mensagem informativa para o usuário.

Se tudo ocorrer bem, é iniciado o tratamento da comunicação bluetooth no dispositivo servidor, iniciando um serviço, esperando um cliente se conectar. Esta lógica é codificada na classe ConeaoThread chamada na linha 18. A classe ConeaoThread é uma classe interna de ServidorBTActivity, sendo esta detalhada na Listagem 4.

Thread são códigos que podem ser executados “em paralelo” com outros códigos. No exemplo apresentado, o código da Thread, este responsável pela comunicação Bluetooth, será executado em paralelo com o código da interface gráfica (classe Activity).

Esta execução em paralelo faz com que os códigos sejam executados de forma independente uma da outra, assim, se acontecer demora na comunicação Bluetooth, por exemplo, ou ainda esta conexão travar, isto não influenciará na interface gráfica, podendo o usuário do aplicativo utilizá-la normalmente, sem perceber tais acontecimentos.


 public class ConeaoThread extends Thread{

       private BluetoothAdapter adapter;  
       private BluetoothServerSocket server;    
 
       public ConeaoThread(BluetoothAdapter adapter)
      throws IOEception{
         this.adapter = adapter;
             server = 
             adapter.listenUsingRfcommWithServiceRecord
             (NAME, MY_UUID);
       }
 
 public void run() {
       try{
             while(true){                                         
                   BluetoothSocket cliente = server.accept();
                   new TratarCliente(cliente).start();      
             }
       }catch(Eception e){
             Toast.makeTet(ServidorBTActivity.this, "Erro:" + 
                         e.getMessage(), Toast.LENGTH_SHORT).show();
         }                 
   }
 }//fim da classe ConeaoThread
Listagem 4. ServidorBTActivity.java – Classe interna ConeaoThread

O código desta listagem trata um servidor multiconexão, ou seja, vários clientes bluetooth podem se conectar ao servidor e este conseguirá responder a requisição de todos eles.

A classe ConeaoThread herda funcionalidades de Thread (linha 01) e declara os objetos do tipo BluetoothAdapter (que representa o adaptador bluetooth do dispositivo, este recuperado na linha 10 da Listagem 4) e um BluetoothServerSocket, responsável por fornecer a possibilidade que clientes se conectem a ela, como se usassem comunicação socket.

O método construtor (linha 06) recebe por parâmetro o BluetoothAdapter, que valoriza o atributo da classe (linha 07), assim como é instanciado o serviço disponibilizado pelo aplicativo servidor. Para criar o serviço, é necessário informar o nome dele e sua identificação única (linha 09).

Feito isso, o método run é acionado, sendo neste executado um loop infinito (linha 14) para tratar as diversas conexões simultâneas dos dispositivos clientes, criando um BluetoothSocket para cada cliente conectado. Desta forma, cada cliente é tratado individualmente pela thread TratarCliente iniciada na linha 16.

Ocorrendo erros no programa, estes são recuperados pelo comando catch (linha 18), apresentando uma mensagem informativa na tela para o usuário.

O código da classe TratarCliente, interna do ServidorBTActivity.java, é apresentada com detalhes na Listagem 5.


  public class TratarCliente etends Thread{

  private BluetoothSocket cliente;

  private InputStream entrada;
  private OutputStream saida;

  private String nomeCliente;
  private String msg;

  public TratarCliente(BluetoothSocket cliente){
        try{
              this.cliente = cliente;
              entrada = cliente.getInputStream(); 
              saida = cliente.getOutputStream();  
        }catch(Eception e){
              Toast.makeTet(ServidorBTActivity.this, 
                "Erro:" + e.getMessage(), 
                Toast.LENGTH_SHORT).show();
        }
  }
  
  public void run(){
   try{
    while(true){                                 
        byte[] buffer = new byte[1024]; 
        int bytes = entrada.read(buffer);   
              msg = new String(buffer);       
              nomeCliente = cliente.getRemoteDevice().getName();
                    
             ServidorBTActivity.this.runOnUiThread( new Runnable() 
              public void run() {
                tvNomeCliente.setTet("Cliente: " + 
                  nomeCliente.toString());
                    tvEntrada.setTet("Mensagem recebida: " + 
                   msg.trim() );
                }
            } );
              Thread.sleep(1000);                            

              saida.write(("Recebido:"+msg).getBytes());           
              saida.flush();                           

         }
        }catch(Eception e){
          Toast.makeTet(ServidorBTActivity.this, "Erro:" + 
          e.getMessage(), Toast.LENGTH_SHORT).show();
        }
      }
    }//fim da classe TratarCliente
}//fim da classe ServidorBTActivity
Listagem 5. ServidorBTActivity.java – Classe interna TratarCliente

A classe TratarCliente também é uma classe interna de ServidorBTActivity, e assim como a classe ConeaoThread, esta classe também herda funcionalidades de Thread (linha 01).

Confira também

Nesta classe são declarados os objetos de BluetoothSocket (linha 03), sendo que este representa a conexão com o cliente, os objetos da classe InputStream e OutputStream (linhas 05 e 06), sendo que estas representam respectivamente os fluxos de entrada e saída do dispositivo, utilizado para a troca de mensagens, e as variáveis auxiliares nomeCliente e mensagem (linhas 08 e 09), que armazenarão os dados referentes a mensagem recebida.

O método construtor da classe (linha 11) valoriza a variável cliente (linha 13), assim como define os fluxos de entrada e saída para troca de mensagens. Caso algum ocorra, o mesmo é tratado na exceção da linha 16).

Por fim, no método run da Thread é realizado um loop infinito (linha 24) afim de tratar inúmeras mensagens enviadas por um único cliente. Desta forma, na linha 25 é criado um array de bytes de um Kbyte (tamanho máximo estimado para as mensagens trafegadas). Após, na linha 26 é aguardada a mensagem vinda do cliente. Assim que a mesma chegar, é convertida em uma String (linha 27) e é recuperado o nome do dispositivo que enviou a mensagem (linha 28).

O ambiente de desenvolvimento Android possui uma limitação, a qual não permite atualizar conteúdo visual dos componentes dentro de Threads que não seja a principal, desta forma, para adicionarmos à tela o conteúdo vindo do dispositivo cliente é necessário executar o comando runOnUiThread (linha 30) o qual apresentará na tela o nome do dispositivo cliente (linha 32) e o texto recebido (linha 33). Como o texto recebido possui um tamanho de um Kbyte (1024 bytes), é necessário retirar os caracteres em branco com o comando trim() (linha 35).

Feito isso, é realizada uma interrupção de 1 segundo (linha 38) para evitar problemas na comunicação (evitar que seja enviada uma outra mensagem sem que a primeira tenha sido processada). Nas linhas 40 e 41 a mensagem de retorno é enviado ao cliente, e uma nova iteração do loop é realizada. Caso ocorra algum erro, o mesmo é tratado no catch da linha 44.

Desenvolvimento do aplicativo cliente

Para exemplificar a comunicação Bluetooth, foi optado pelo desenvolvimento de um aplicativo simples, onde o servidor receberá uma mensagem de texto do aplicativo cliente, devolvendo-o para o mesmo. O aplicativo servidor foi apresentado anteriormente, agora será apresentado o código do aplicativo cliente, este possuindo uma tela para o envio/recepção dos dados do servidor, além de uma segunda tela para pesquisa do servidor (apresentando os dispositivos Bluetooth próximos).

O aplicativo cliente é relativamente mais complexo do que o aplicativo servidor, pois ao contrário do servidor que só disponibiliza um serviço, no aplicativo cliente deve-se codificar a lógica para localizar o servidor para só então realizar a conexão. Desta forma, o aplicativo cliente possui dois Activities. O primeiro será o Activity principal, responsável pela conexão com o servidor e a troca de informações, o segundo será o Activity de localização de dispositivos bluetooth, ambos os Activities são criados em um projeto com o nome de ClienteBT.

Na Listagem 6 temos o código da interface gráfica do Activity principal do cliente.

Embora na plataforma Android existem inúmeros tipos de componentes visuais para entrada, processamento e saída de dados, na maioria das aplicações onde o objetivo é mostrar outras funcionalidades (como esta que apresenta a comunicação Bluetooth) são utilizados apenas os componentes EditText para entrada de dados, Button para o processamento e TextView para saída, embora um usuário com conhecimento da plataforma poderia utilizar inúmeros recursos visuais para o desenvolvimento deste aplicativo.


 <?ml version="1.0" encoding="utf-8"?>
 <LinearLayout mlns:android=
   "http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:orientation="vertical" >

 <Button
         android:id="@+id/btConectar"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:tet="Conectar" />

 <TetView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:tet="Teto:" />

 <EditTet
         android:id="@+id/etSaida"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:hint="Digite aqui o seu teto" />

 <Button
         android:id="@+id/btEnviar"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:tet="Enviar" />

 <TetView
         android:id="@+id/tvServidor" 
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:tet="Servidor:" />

 <TetView
         android:id="@+id/tvResposta"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:tet="Resposta:" />
 
 </LinearLayout>
Listagem 6. activity_cliente_bt.xml – Interface gráfica do Activity principal do cliente

A interface da Listagem 6 tem algumas similaridades, entre elas o uso do LinearLayout (linha 02) e a utilização de componentes simples da plataforma Android.

Assim, na linha 07 é declarado um botão, cuja função é apresentar uma nova tela com todos os dispositivos bluetooth disponíveis. O TextView seguinte (linha 13) é o rótulo para o texto que o usuário poderá digitar no cliente, já o componente EditTet (linha 18) receberá o texto digitado pelo usuário, o qual será enviado ao aplicativo servidor.

Finalizando a interface gráfica, o componente Button (linha 24) é responsável por enviar o texto digitado no EditText para o aplicativo servidor. Já os dois TextView (linhas 30 e 36) apresentam para o usuário do aplicativo cliente o nome do dispositivo servidor ao qual será enviada a mensagem e a resposta do servidor.

Após codificada a interface gráfica, o próximo passo é codificar a Activity principal do programa cliente, sendo este código apresentado na Listagem 7.


 package br.com.devmedia.clientebtactivity;

 import java.io.IOEception;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.UUID;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothSocket;

 import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
 import android.view.View;
 import android.widget.Button;
 import android.widget.EditTet;
 import android.widget.TetView;
 import android.widget.Toast;

 public class ClienteBTActivity etends Activity {
     private EditTet etTeto;
     private TetView tvResposta;
     private TetView tvServidor;
     private Button btConectar;
     private Button btEnviar;
 
     private static final int RECUPERA_DISPOSITIVO = 0;
     public static final UUID BTUUID = 
         UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
 
     private String nomeDispositivo;
     private String enderecoDispositivo;
 
     private BluetoothAdapter btAdapter;
     private BluetoothDevice mmDevice;  
     private BluetoothSocket servidor;  
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_cliente_bt);
 
         etTeto = (EditTet)findViewById(R.id.etSaida);
         tvResposta = (TetView)findViewById(R.id.tvResposta);
         tvServidor = (TetView)findViewById(R.id.tvServidor);
         btConectar = (Button)findViewById(R.id.btConectar);
         btEnviar = (Button)findViewById(R.id.btEnviar);
 
         //código conexão Bluetooth Aqui
 
         btConectar.setOnClickListener(new View.OnClickListener() {
         @Override
         public void onClick(View v) {
               btConectarOnClick();                     
         }
   });
 
         btEnviar.setOnClickListener(new View.OnClickListener() {
         @Override               
         public void onClick(View arg0) {
               btEnviarOnClick();
         }

   });
     }

 protected void btConectarOnClick() {
   // TODO Auto-generated method stub
   
 }
 

 private void btEnviarOnClick() {
   // TODO Auto-generated method stub
   
  }      
}//fim da classe ClienteBTActivity
Listagem 7. ClienteBTActivity.java – Interface gráfica do Activity principal do cliente

Das linhas 03 a 09 são importadas as classes necessárias para a comunicação bluetooth. Já das linhas 11 a 18 são importadas as classes referentes à interface gráfica/ciclo de vida da aplicação Android.

Uma aplicação para dispositivos móveis, como as desenvolvidas usando tecnologias Android ou Java ME, possuem a necessidade de controlar o ciclo de vida, pois ao contrário das aplicações desktop, nas aplicações móveis não é usual possuir alternância de contexto (Alt+Tab das aplicações Windows, por exemplo). Desta forma, quando o aplicativo está sendo executado e recebemos uma ligação, por exemplo, automaticamente nosso aplicativo é parado, ou então, quando é iniciada uma nova Activity, a Activity chamada entra em estado de pausa.

Dos métodos do ciclo de vida, o único obrigatório é o onCreate(), executado quando um aplicativo é iniciado. Os demais métodos são opcionais, como o onPause() e onResume() – chamado quando a aplicação entra e volta de um estado de pausa, e os métodos onStop(), onRestart() e onStart(), o primeiro chamado quando a aplicação entra em estado de parada e os dois últimos executados quando retornam deste estado.

A classe ClienteBTActivity é uma Activity tradicional (linha 20), sendo declarados os seus componentes visuais (linhas 21 a 25), além de variáveis de controle do aplicativo.

A primeira variável criada (na verdade uma constante, pois possui o identificador final) é o RECUPERA_DISPOSITIVOS (linha 27), sendo utilizada para a troca de informações entre esta classe e a classe que lista os dispositivos bluetooth próximos ao cliente. Também é criada uma constante chamada BTUUID que possui o identificador único do serviço no qual queremos nos conectar (lembre-se que este identificador também foi declarado no aplicativo servidor).

Seguindo as declarações, duas Strings são necessárias (linhas 31 e 32), a primeira para armazenar o nome do dispositivo servidor e a segunda para armazenar seu endereço.

Por fim, a declaração de um objeto da classe BluetoothAdapter (linha 34), o qual representa a interface bluetooth do dispositivo, um objeto do tipo BluetoothDevice (linha 35), o qual conterá dados do dispositivo remoto (neste caso, o dispositivo servidor), e um objeto de BluetoothSocket (linha 36), este para o gerenciamento do envio e recepção dos dados.

No método onCreate (linha 39) é iniciado o Activity (linhas 40 e 41), sendo recuperados os componentes visuais (das linhas 43 a 47). A linha 49 receberá o código para conexão bluetooth, porém, este será detalhado na Listagem 8. Por fim, das linhas 51 a 56 é tratado o evento de clique no botão Conectar, e das linhas 58 a 64 o tratamento de clique do botão Enviar, sendo que estes executam respectivamente os métodos btConectarOnClick (linha 67) e btEnviarOnClick (linha 73).

O código da Listagem 8 apresenta a recuperação da interface bluetooth. Tal código deve ser inserido no local da linha 49 da Listagem 7.


 btAdapter = BluetoothFactory.getBluetootAdapter();

 if(!btAdapter.isEnabled()){
    Intent enableIntent = new 
     Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
      startActivityForResult(enableIntent, 2);
 }
Listagem 8. ClienteBTActivity.java – Recuperação da interface Bluetooth

A recuperação da interface Bluetooth acontece na linha 01, utilizando para isso uma classe de Factory (também conhecido como classe Singleton). O objetivo desta classe é evitar redundância na instanciação de um mesmo objeto. O código completo da Singleton BluetoothFactory é apresentado na Listagem 9.

Por fim, como aconteceu no programa servidor, é verificado se o bluetooth do dispositivo está acionado (linha 03), caso não esteja, o mesmo é iniciado (linhas 4 a 6).


import android.bluetooth.BluetoothAdapter;

public class BluetoothFactory {

 private static BluetoothAdapter btAdapter;
 
 public static BluetoothAdapter getBluetootAdapter() {
       if ( btAdapter == null ) {
             btAdapter = BluetoothAdapter.getDefaultAdapter();
       }           
       return btAdapter;
 }     
}
Listagem 9. ClienteBTActivity.java – Recuperação da interface Bluetooth

A classe Singleton apresentada na Listagem 9 importa (linha 01) e declara (linha 05) um objeto do tipo BluetoothAdapter. No método estático getBluetoothAdapter (linha 07) é verificado se o objeto btAdapter foi instanciado (linha 08), caso não esteja instanciado, o mesmo é recuperado (linha 09). Ao final da lógica, o objeto é retornado ao chamador (linha 11). Isso garante que o objeto btAdapter, do tipo BluetoothAdapter, seja instanciado apenas uma vez.

Com o código apresentado até o momento, o aplicativo já pode ser executado, sendo recuperada a interface bluetooth e apresentada a tela inicial do aplicativo. Se o usuário clicar no botão Conectar, o código da Listagem 10 deve ser executado.


 public void btConectarOnClick(){
   Intent i = new 
   Intent(ClienteBTActivity.this,EscolhaDispositivo.class);
   startActivityForResult(i, RECUPERA_DISPOSITIVO);
 }
Listagem 10. ClienteBTActivity.java – Tratamento de clique do botão Conectar

O método btConectarOnClick (linha 01) é executado no clique do botão Conectar, e esse instancia uma classe de intenção (Intent) – linha 02, passando por parâmetro uma instância da Activity chamadora e da Activity que será chamada. Depois disso, essa intenção é usada como parâmetro do método startActivityForResult, que chama a nova Activity e espera uma resposta deste. Como identificador de chamada foi utilizada a constante RECUPERA_DISPOSITIVO, declarada anteriormente.

A interface gráfica da Activity EscolherDispositivo, bem como sua classe é apresentada respectivamente nas Listagens 11 e 12.


 <?ml version="1.0" encoding="utf-8"?>
 <LinearLayout mlns:android=
   "http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical" >
  
  <ListView
  android:id="@+id/lvDispositivos"
  android:layout_width="fill_parent"
  android:layout_height="200dp"
  android:layout_weight="0.82"
  android:clickable="true" />
 
  <Button
  android:id="@+id/btRecarregar"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:layout_gravity="bottom"
  android:hint="Renova lista de bluetooth"
  android:tet="Buscar" />
 
 </LinearLayout>
Listagem 11. activity_escolha_dispositivo.xml – Interface gráfica da tela de seleção de dispositivo

O código apresentado é simples, onde o formato do Layout é Linear (linha 02) e vertical (linha 05), sendo declarado um ListView (linha 07) onde serão apresentados os dispositivos bluetooth próximos, e um botão (linha 14), podendo este ser utilizado para “recarregar” os dispositivos bluetooth próximos.

Apesar de extenso, o código apresentado na Listagem 12 é relativamente simples. Ele é responsável por percorrer todos os dispositivos bluetooth próximos do aplicativo cliente, adicionando-os em uma lista. O usuário tem a opção de clicar sobre o dispositivo para realizar a conexão ou clicar sobre o botão recarregar, o qual limpa a lista e volta a pesquisar novos dispositivos.

As linhas de 01 a 16 são responsáveis pelos imports do programa, sendo este um Activity tradicional (linha 18). As linhas seguintes declaram a constante para troca de informação entre aplicativos (linha 20), a interface Bluetooth (linha 22), uma lista na qual serão adicionados os dispositivos bluetooth que serão apresentados (linha 23) e um HashMap contendo o endereço de cada dispositivo bluetooth (linha 24).

No método onCreate é iniciado o Activity (linhas 27 e 28), assim como recuperados os componentes visuais da lista e do botão (linhas 30 a 32). O ArrayAdapter que formatará os dados da lista também é criado (linha 38), assim como o HashMap com os endereços (linha 40).

Seguindo a lógica, a interface bluetooth é recuperada (linha 42), sendo também declarado o tratamento do evento de clique nos elementos da lista (linhas 48 a 53). Este executa o método lvDispositivosOnItemClick (linha 51), apresentado com detalhes posteriormente.


 import factory.BluetoothFactory;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.content.BroadcastReceiver;
 
 import android.app.Activity;
 import android.content.Contet;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Bundle;
 import java.util.HashMap;
 import android.view.View;
 import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
 import android.widget.Button;
 import android.widget.ListView;
 
 public class EscolhaDispositivo etends Activity{
 
  private static final int RECUPERA_DISPOSITIVO = 0;
 
  private BluetoothAdapter btAdapter;
  private ArrayAdapter<String> lvDispositivosAdapter; 
  private HashMap<String, String> address;                   
 
  public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_escolha_dispositivo);
 
        ListView lvDispositivos = 
       (ListView)findViewById(R.id.lvDispositivos);
        Button btnRecarregar =    
       (Button)findViewById(R.id.btRecarregar);
 
        lvDispositivosAdapter = new 
        ArrayAdapter<String>(EscolhaDispositivo.this, 
                   android.R.layout.simple_list_item_1);
 
        lvDispositivos.setAdapter(lvDispositivosAdapter);
 
        address = new HashMap<String,String>();
 
        btAdapter = BluetoothFactory.getBluetootAdapter();
 
        lvDispositivos.setOnItemClickListener(new 
       ListView.OnItemClickListener(){
 
              @Override
              public void onItemClick(AdapterView<?> lista, View v,
              int id, long l){
 
                    lvDispositivosOnItemClick( lista, id );
              }
        });
 
        registerReceiver( ActionFoundReceiver, new 
        IntentFilter(BluetoothDevice.ACTION_FOUND ) );
 
        procurarDispositivos();
 
        btnRecarregar.setOnClickListener(new View.OnClickListener() {
 
              public void onClick(View v) {
                    procurarDispositivos();
              }
        });
  }
Listagem 12. Activity responsável pela procura e seleção de dispositivos Bluetooth – Parte 1

Na linha 55 está o código mais importante da Activity. Nesta linha é informado qual Listener procurará por dispositivos próximos, sendo passado por parâmetro a classe ActionFoundReceiver, está codificada adiante, e também um filtro, o que permite que a classe citada seja chamada apenas quando um novo dispositivo for encontrado.

Após isso, é executado o método responsável por iniciar as buscas (linha 58), assim como o tratamento do botão recarregar (linhas 60 a 64), o qual executa o mesmo código apresentado na linha 58.

Já na Listagem 13, o método procurarDispositivo (linha 70) inicia cancelando eventuais pesquisas que já estejam ocorrendo (linha 02), limpa os elementos que já estejam na lista apresentada para o usuário (linha 03), limpa o HashMap com os endereços (linha 04) e inicia um novo serviço de descoberta utilizando a interface bluetooth do dispositivo (05). Ao ser iniciado o serviço de descoberta, é executada a classe informada no comando registerReceiver (linha 55).

Este executa o método onReceive (linha 97) da classe BroadcastReceiver (linha 93). Inicialmente é recuperado o tipo de operação que chamou o método (linha 99). Se a operação for o resultado de uma pesquisa (linha 101), é recuperada então uma referência do dispositivo (linha 102) e se a lista da tela existir (diferente de null – linha 105), na mesma é acrescentada o nome do dispositivo (linha 106), assim como o endereço no HashMap (linha 107), sendo notificado o componente visual que seu conteúdo foi alterado (linha 108).


   
    
   
    private void procurarDispositivos() {
   
          btAdapter.cancelDiscovery();
          lvDispositivosAdapter.clear();
          address.clear();
          btAdapter.startDiscovery();
    }
   
    
   
    protected void lvDispositivosOnItemClick(AdapterView<?> lista, int id) {
   
          String dispositivoSelecionado =  
          (String)lista.getItemAtPosition(id);
   
          Intent i = getIntent();
          i.putEtra("nome",dispositivoSelecionado);
          i.putEtra("endereco",(String)  
           address.get(dispositivoSelecionado));
          setResult(RECUPERA_DISPOSITIVO,i);
   
          finish();
    }
   
   
    public final BroadcastReceiver ActionFoundReceiver = new 
    BroadcastReceiver(){
   
          @Override
          public void onReceive(Contet contet, Intent intent) {
   
                String action = intent.getAction();
    
                if(BluetoothDevice.ACTION_FOUND.equals(action)) {
          BluetoothDevice device = 
               intent.getParcelableEtra(BluetoothDevice.ETRA_DEVICE);
    
          if(lvDispositivosAdapter != null) {
            lvDispositivosAdapter.add(device.getName());
                address.put(device.getName(),device.getAddress());
            lvDispositivosAdapter.notifyDataSetChanged();
          }
    
                if(BluetoothAdapter.ACTION_DISCOVERY
                  _FINISHED.equals(action)){
                      lvDispositivosAdapter.add("Finalizada a busca.");
                 }
           }
        }
      };//fim da classe ActionFoundReceiver
  117.  }//Fim da classe EscolhaDispositivoActivity
Listagem 13. Activity responsável pela procura e seleção de dispositivos Bluetooth – Parte 2

Por fim, é verificado se terminou a operação de pesquisa (linha 112), caso isso ocorra, uma mensagem é apresentada ao final do componente visual de lista.

Quando for apresentado o dispositivo servidor na tela do dispositivo, o usuário pode clicar sobre ele, o que chamará o método lvDispositivosOnItemClick (linha 80). Neste método é recuperado da tela o elemento selecionado (linha 82), e iniciada a intenção para retornar ao Activity principal (linha 84) passando como parâmetro o nome e o endereço do dispositivo (linhas 85 e 86). Por fim, estes dados são passados (linha 87) e o processamento do Activity de lista é finalizado (linha 89).

Uma vez retornado ao Activity principal, o método onActivityResult() é executado. O método onActivityResult é apresentado na Listagem 14.


     public void onActivityResult(int requestCod,
      int resultCode, Intent data){
       if ( requestCod == RECUPERA_DISPOSITIVO ){
           nomeDispositivo = data.getEtras().getString("nome");
           enderecoDispositivo = data.getEtras().getString("endereco");   
       }
   
       if (conectarDispositivo(enderecoDispositivo)){
           String nomeServidor = "Conectado com: " + 
                    m.mDevice.getName().toString();
           tvServidor.setTet("Servidor: " + nomeServidor);                  
           Toast.makeTet( this, "Conectado com "+nomeDispositivo+"!", 
                  Toast.LENGTH_SHORT ).show();                                   
        }else{
         Toast.makeTet( this, "Não foi possível estabelecer coneão!", 
                    Toast.LENGTH_SHORT ).show();                           
        }
  
  }
Listagem 14. ClienteBTActivity.java – Método onActivityResult, chamado no retorno do EscolhaDispositivo.java

Ao ser executado, o método verifica o requestCode para saber se é o mesmo utilizado na chamada da nova tela (linha 02). Caso isto ocorra, o parâmetro data (do tipo Intent) está valorizado com informações do dispositivo selecionado, como o nome do dispositivo e seu endereço, este armazenado em variáveis do tipo String (linhas 03 e 04).

Após, na linha 07 é tentada a conexão com este dispositivo (o código do método conectarDispositivo é apresentado em detalhes na Listagem 15). Havendo a conexão, é formatada uma String com o nome do servidor no qual houve a conexão (linha 08), além de apresentar os dados no TextField (linha 10), finalizando com uma mensagem informativa via componente Toast (linha 11).

Caso a conexão não seja bem sucedida, uma mensagem informativa é apresentada ao usuário (linha 15). Na Listagem 15, o código responsável pela conexão com o servidor bluetooth é apresentado.


 private boolean conectarDispositivo(String enderecoDispositivo) {
   mmDevice = btAdapter.getRemoteDevice(enderecoDispositivo);
   BluetoothSocket tmp;
   
   try{
         tmp = mmDevice.createRfcommSocketToServiceRecord(BTUUID);
   }catch(Eception e){
         return false;
   }
   
   servidor = tmp;                    
   btAdapter.cancelDiscovery();
   
   try{
         servidor.connect();
   }catch(IOEception connectEcption){
         try{
               servidor.close();
         }catch(IOEception closEception){
               return false;
         }
   }
   
   return true;
 }
Listagem 15. ClienteBTActivity.java – Método conectarDispositivo, o qual estabelece conexão com o servidor Bluetooth

O método acima inicia tentando a recuperação do dispositivo remoto, este identificado pelo seu endereço (linha 02). Havendo a recuperação, o próximo comando é a recuperação dos fluxos de dados. Esta acontece a partir da instanciação de um BluetoothSocket (linha 06), sendo na sequência finalizado qualquer serviço de descoberta (linha 12). Por fim, é realizada a conexão com o servidor (linha 15), retornando false se algum erro ocorrer (linha 20).

Neste ponto, o aplicativo cliente já está quase completo, já funcionando o procedimento para pesquisar por dispositivo, selecionar e conectar a ele. Agora apresentaremos o último processo faltante que é o envio das informações (ver Listagem 16).

O envio dos dados acontece quando o usuário clicar no botão Enviar da interface gráfica. Este método chama o btEnviarOnClick(linha 01), que cria os fluxos de entrada e saída de dados (linha 07 e 08).

Para o envio dos dados digitados no EditText, o mesmo é armazenado em uma variável String (linha 10) e escrito no fluxo de saída (linhas 12 e 13). Na sequência é criado um array de bytes de 1Kbyte (linha 15) para recuperar a resposta do servidor (linha 16), a qual é convertida em String (linha 17). Também é recuperado o nome do servidor (linha 18 e 19) para a apresentação na tela. Por fim, os dados recuperados são apresentados nos componentes TextView (linha 21 e 22).

Caso ocorra algum erro, uma exceção é capturada na linha 24, apresentando uma mensagem informativa para o usuário (linha 25 e 26).


  private void btEnviarOnClick() {
    
    InputStream entrada;
    OutputStream saida;
  
    try{
          saida = servidor.getOutputStream();.
                entrada = servidor.getInputStream().;
          
                String msgEnviar = etTeto.getTet().toString(); 
          
                saida.write(msgEnviar.getBytes());
                saida.flush();                           
          
                byte[] buffer = new byte[1024];    
                int bytes = entrada.read(buffer);  
                String msgResposta = new String(buffer);
                String nomeServidor = 
                            servidor.getRemoteDevice().getName();
          
                tvResposta.setTet(msgResposta);
                tvServidor.setTet(nomeServidor);
  
    }catch(Eception e){
          Toast.makeTet(this, "Erro: " + e.getMessage(), 
                     Toast.LENGTH_SHORT).show(); 
    }
    
  }
Listagem 16. ClienteBTActivity.java – Método btEnviarOnClick

Testando o aplicativo Bluetooth

Testar uma aplicação móvel não é uma tarefa tão trivial quanto testar uma aplicação desktop ou web, pois, em muitos casos, os recursos existentes nos smartphones celular não estão presentes nos computadores desktop. São exemplos os processos para realizar ligações e enviar mensagens SMS, utilização de sensores e a comunicação entre dispositivos.

Dada a limitação dos emuladores do Android para testar a comunicação bluetooth, o mesmo não pode ser testado diretamente no emulador. Desta forma, a maneira mais segura e eficiente de realizar os testes é no próprio aparelho Android.

Para executar a aplicação no aparelho existem duas maneiras, a primeira é gerar o arquivo apk (Android Application Package), copiando este para dentro do dispositivo móvel para execução. A segunda maneira é utilizar um dispositivo Android como emulador, ou seja, dentro da sua IDE você pode iniciar o aplicativo no seu dispositivo Android. Para isso basta instalar via Android SDK Manager a ferramenta Google USB Driver. Depois disso, é preciso conectar o dispositivo Android via USB no computador, habilitando o Debug via USB no dispositivo (via menu de Configurações – Aplicações – Desenvolvimento e habilita a opção Depuração de USB). Por fim, ao executar a aplicação Android, na tela de seleção da AVD (Android Virtual Device), escolha seu dispositivo Android conectado ao computador via USB.

Conclusão

Este artigo apresentou conceitos da comunicação bluetooth, um dos recursos mais poderosos dos dispositivos móveis que permite a troca de informações entre dispositivos (usando uma rede sem cabo e de baixo consumo de energia). Isto muitas vezes permite contornar algumas limitações dos dispositivos (falta de impressora, falta de um leitor de código de barra, ausência de GPS, etc.).

No exemplo apresentado, uma aplicação simples foi desenvolvida afim de apresentar os conceitos nas classes de comunicação bluetooth. Foi possível abordar conceitos como iniciação do serviço bluetooth, procura por dispositivos, conexão, envio e recepção de mensagens de texto.

Confira também