Este post é uma continuação.
Parte I – Tutorial Android – Desenvolvendo Com Bluetooth Para Comunicação Veicular
Parte II – Tutorial Android – Desenvolvendo Com Bluetooth Para Comunicação Veicular
5. Habilitando descoberta
Se você deseja fazer o dispositivo local detectável por outros dispositivos, chame startActivityForResult (ACTION_REQUEST_DISCOVERABLE, int). Isso irá emitir um pedido para ativar o modo detectável através das configurações do sistema (sem parar a sua aplicação). Por padrão, o dispositivo se tornará detectável por 120 segundos. Você pode definir uma duração diferente, adicionando a intent EXTRA_DISCOVERABLE_DURATION. A duração máxima que um aplicativo pode definir é de 3.600 segundos e o valor 0 significa que o dispositivo é sempre detectável. Qualquer valor abaixo de 0 ou acima de 3600 é automaticamente definida para 120 segundos). Por exemplo, esse trecho define a duração de 300:
[sourcecode language=”java”]
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
startActivity(discoverableIntent);
[/sourcecode]
Figura 2: A caixa de diálogo descoberta de habilitação.
Uma caixa de diálogo será exibida, solicitando a permissão do usuário para tornar o dispositivo detectável, como mostrado na Figura 2. Se o usuário responder “Sim”, o dispositivo se tornará detectável durante o período de tempo especificado. Sua atividade receberá então uma chamada para o retorno onActivityResult (), com o código do resultado igual ao tempo que o dispositivo está detectável. Se o usuário respondeu “Não” ou se ocorrer um erro, o código de resultado será RESULT_CANCELED .
O dispositivo permanecerá no modo de descoberta no tempo previsto. Se você desejar ser notificado quando o modo de descoberta alterar, você pode registrar um BroadcastReceiver para a intent ACTION_SCAN_MODE_CHANGED. Este irá conter os campos extras EXTRA_SCAN_MODE e EXTRA_PREVIOUS_SCAN_MODE, que indica o modo de digitalização atual e antigo, respectivamente. Os valores possíveis para cada um são SCAN_MODE_CONNECTABLE_DISCOVERABLE, SCAN_MODE_CONNECTABLE, ou SCAN_MODE_NONE, que indicam que o dispositivo está no modo de descoberta, fora do modo de descoberta, mas ainda capaz de se conectar, ou fora do modo de descoberta e incapaz de se conectar, respectivamente.
Não é necessário habilitar a descoberta de dispositivo se você for iniciar uma conexão a um dispositivo remoto. Habilitar a descoberta só é necessário quando você quiser que seu aplicativo hospede um servidor socket que irá aceitar conexões de entrada.
6. Conexão entre dispositivos
Para criar uma conexão entre dois dispositivos, você deve implementar tanto o servidor quanto o cliente, pois um dispositivo deve abrir um servidor socket e o outro deve iniciar a conexão (usando o endereço do dispositivo servidor MAC para iniciar uma conexão). O servidor e o cliente são considerados conectados entre si quando eles estão conectados pelo BluetoothSocket no mesmo canal RFCOMM. Neste ponto, cada dispositivo pode obter fluxos de entrada e saída e a transferência de dados pode começar, que é discutido na seção sobre gestão de uma conexão . Esta seção descreve apenas como iniciar a conexão entre dois dispositivos.
O dispositivo servidor e o dispositivo cliente obtêm o BluetoothSocket de diferentes maneiras. O servidor irá recebê-lo quando a conexão de entrada for aceita. O cliente irá recebê-lo quando ele abre um canal RFCOMM para o servidor.
Uma técnica de aplicação é para preparar automaticamente cada dispositivo como um servidor, de modo que cada um possui um soquete de servidor aberto e aguardando as conexões. Em seguida, um ou outro dispositivo pode iniciar uma ligação com o outro e tornar-se o cliente. Alternativamente, um dispositivo pode explicitamente “hospedar” a conexão e abrir um servidor socket e o outro dispositivo pode simplesmente iniciar a conexão.
Nota: Se os dois dispositivos não tiverem sido anteriormente pareados, o Android mostrará automaticamente uma notificação de pedido de pareamento por uma caixa de diálogo para o usuário durante o procedimento de conexão, como mostrado na Figura 3. Assim, ao tentar conectar dispositivos, o aplicativo não precisa se preocupar se os dispositivos estão ou não pareados. Sua tentativa de conexão RFCOMM será bloqueada até que o usuário tenha pareado ou falhará se o usuário rejeitar o pareamento, se o pareamento falhar ou se estourar o tempo limite.
Figura 3: A caixa de diálogo de pareamento Bluetooth
6.1. Conectar-se como um servidor
Quando você quiser conectar dois dispositivos, é preciso atuar como um servidor, mantendo aberto o “diálogo” BluetoothServerSocket . O objetivo do socket do servidor é para escutar solicitações de conexão de entrada e quando um é aceito, fornecer uma ligação BluetoothSocket . Quando o BluetoothSocket é adquirido a partir do BluetoothServerSocket , o BluetoothServerSocket pode (e deve) ser descartado, a menos que queira aceitar novas conexões.
Sobre o UUID
Um identificador universalmente exclusivo (UUID) tem um formato de 128-bit padronizado para uma string ID usado para identificar informações. A questão de um UUID é que ele é extenso o suficiente para que você possa selecionar aleatoriamente qualquer um que não irá colidir. Neste caso, ele é usado para identificar exclusivamente o serviço de seu aplicativo Bluetooth. Para obter um UUID para usar com o aplicativo, você pode usar um dos muitos geradores aleatórios UUID na web, em seguida, inicializar o UUID com fromString (String) .
Aqui é o procedimento básico para configurar um servidor socket e aceitar uma conexão:
1. Obter uma BluetoothServerSocket chamando o listenUsingRfcommWithServiceRecord (String, UUID) .
A seqüência é um nome de identificação do seu serviço, que o sistema irá automaticamente gravar a entrada de dados no dispositivo em um novo Service Discovery Protocol (SDP) (o nome é arbitrário e pode ser simplesmente o nome do aplicativo). O UUID é também incluído na entrada SDP e irá ser a base para a ligação com o dispositivo cliente. Ou seja, quando o cliente tenta se conectar com este dispositivo, ele levará um UUID que identifica o serviço com o qual deseja se conectar. Estes UUIDs deve corresponder a fim de que a ligação seja aceita (no passo seguinte).
2. Comece a escutar os pedidos de conexão chamando accept (). Esta é uma chamada de bloqueio. Ele irá retornar quando uma conexão for aceita ou ocorrer uma exceção. A conexão é aceita somente quando um dispositivo remoto enviar um pedido de conexão com um UUID correspondente ao registrado para este socket servidor receber. Quando bem sucedida, accept () irá retornar uma ligação BluetoothSocket .
3. A menos que você deseja aceitar conexões adicionais, chame close(). Isso libera o socket servidor e todos os seus recursos, mas fechará o conectado BluetoothSocket que foi retornado por accept(). Ao contrário do TCP/IP, RFCOMM só permite que um cliente esteja conectado a um canal por vez, então na maioria dos casos, faz sentido chamar close() no BluetoothServerSocket imediatamente depois de aceitar e conectar um socket.
O accept() não deve ser executado na Activity main, porque é uma chamada de bloqueio e impedirá qualquer outra interação com o aplicativo. Ele geralmente faz sentido fazer todo o trabalho com um BluetoothServerSocket ou BluetoothSocket em um novo método gerenciado pelo seu aplicativo. Para cancelar uma chamada bloqueada como accept(), chame close() no BluetoothServerSocket (ou BluetoothSocket) de outra thread e a chamada bloqueado retornará imediatamente. Note que todos os métodos em um BluetoothServerSocket ou BluetoothSocket são thread-safe.
Exemplo
Aqui está uma thread simplificada para o servidor que aceita conexões de entrada:
[sourcecode language=”java”]
private class AcceptThread extends Thread {
private final BluetoothServerSocket mmServerSocket;
public AcceptThread() {
// Use um objeto temporario que é depois atribuido a mmServerSocket,
// porque mmServerSocket é final
BluetoothServerSocket tmp = null;
try {
// MY_UUID é a string UUID, também usada pelo cliente
tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
} catch (IOException e) { }
mmServerSocket = tmp;
}
public void run() {
BluetoothSocket socket = null;
// Continuar recebendo até ocorrer uma exceção ou o retorno de um socket
while (true) {
try {
socket = mmServerSocket.accept();
} catch (IOException e) {
break;
}
// Se a conexão for aceita
if (socket != null) {
// Trabalhar para gerir a conexão (em uma thread separada)
manageConnectedSocket(socket);
mmServerSocket.close();
break;
}
}
}
/** Cancelará o socket de espera, e terminará o método */
public void cancel() {
try {
mmServerSocket.close();
} catch (IOException e) { }
}
}
[/sourcecode]
Neste exemplo, apenas uma ligação de entrada é desejada, então, logo que uma conexão for aceita e o BluetoothSocket for adquirido, a aplicação envia o BluetoothSocket adquirida para um método separado, fecha o BluetoothServerSocket e quebra do laço.
Note que quando accept () retorna o BluetoothSocket , o soquete já está conectado, por isso você não deve chamar connect ().
manageConnectedSocket()
é um método fictício do aplicativo que iniciará a discussão para a transferência de dados, que são discutidos na seção sobre a gestão de uma conexão .
Normalmente deve fechar o BluetoothServerSocket assim que forem feitas as conexões. Neste exemplo, close() é chamado logo que o BluetoothSocket é adquirido. Você também pode querer fornecer um método público em sua thread que pode fechar o privado BluetoothSocket no evento que você precisa parar de ouvir no socket servidor.
6.2. Conectar-se como um cliente
Para iniciar uma conexão com um dispositivo remoto, você deve primeiro obter um objeto BluetoothDevice que representa o dispositivo remoto. (Conseguir um BluetoothDevice é abordado na seção acima sobre Dispositivos Encontrando). Você deve então usar o BluetoothDevice para adquirir um BluetoothSocket e iniciar a conexão.
Aqui está o procedimento básico:
- Usando o BluetoothDevice , pegue o BluetoothSocket chamando createRfcommSocketToServiceRecord (UUID). Isso inicializa um BluetoothSocket que irá se conectar ao BluetoothDevice . O UUID passado aqui deve coincidir com o UUID usado pelo dispositivo servidor quando ele abriu sua BluetoothServerSocket (com listenUsingRfcommWithServiceRecord (String, UUID) ). Usar o mesmo UUID é simplesmente uma questão de codificar a seqüência de UUID em sua aplicação para, em seguida, fazer referência a ele, tanto no servidor quanto no código do cliente.
- Iniciar a conexão chamando connect ().
Após essa chamada, o sistema irá realizar uma pesquisa SDP no dispositivo remoto, a fim de coincidir com o UUID. Se a pesquisa for bem-sucedida e o dispositivo remoto aceitar a conexão, ele irá compartilhar o canal RFCOMM para usar durante a conexão e connect() irá retornar. Este método é uma chamada de bloqueio. Se, por qualquer motivo, a conexão falhar ou o método connect() ficar fora (após cerca de 12 segundos), então ele lançará uma exceção.
Por connect() ser uma chamada de bloqueio, este procedimento de conexão deve ser sempre realizada em uma thread separada da atividade principal.
Nota: Você deve sempre garantir que o dispositivo não está efetuando a descoberta do dispositivo quando você chamar connect (). Se a descoberta estiver em progresso, na seguida, a tentativa de conexão será significativamente retardada e é mais provável é falhar.
Exemplo Aqui está um exemplo básico de uma thread que inicia uma conexão Bluetooth:
[sourcecode language=”java”]private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
public ConnectThread(BluetoothDevice device) {
// Usar um objeto temporário que depois sera atribuído a mmSocket,
// porque mmSocket sera o valor final
BluetoothSocket tmp = null;
mmDevice = device;
// Obter uma BluetoothSocket para conectar com o BluetoothDevice dado
try {
// MY_UUID é string UUID do aplicativo, também usado pelo código do servidor
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) { }
mmSocket = tmp;
}
public void run() {
// Cancelar descoberta porque irá desacelerar a conexão
mBluetoothAdapter.cancelDiscovery();
try {
// Conectar o dispositivo através do socket.
// Isto será bloqueado até ser bem sucedido ou lançar uma exceção</div>
<pre>mmSocket.connect();
} catch (IOException connectException) {
// Não foi possível conectar; fechar o soquete e sair
try {
mmSocket.close();
} catch (IOException closeException) { }
return;
}
// Gerenciar a conexão (em uma thread separada)
manageConnectedSocket(mmSocket);
}
// Cancelar a conexão em andamento e fechar o socket
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
[/sourcecode]
Observe que cancelDiscovery() é chamado antes que a conexão seja feita. Você deve sempre fazer isso antes de ligar e ele é seguro para chamar sem realmente verificar se ele está sendo executado ou não (mas se você quiser conferir, chame a isDiscovering ()).
manageConnectedSocket()
é um método fictício do aplicativo que irá iniciar a discussão para a transferência de dados, que são discutidos na seção sobre a gestão de uma conexão .
Quando você é feito com seu BluetoothSocket , sempre chamar close() para limpar. Se o fizer, será imediatamente fechar o socket conectado e limpar todos os recursos internos.
7. Gerenciando uma conexão
Depois de ter conectado dois (ou mais) dispositivos, cada um terá uma conexão BluetoothSocket. Aqui é onde começa a diversão, pois você pode compartilhar dados entre dispositivos. Usando o BluetoothSocket, o procedimento geral para transferir dados arbitrários é simples:
- Obter o InputStream e OutputStream que lidam com transmissões através do socket, através de getInputStream () e getOutputStream (), respectivamente.
- Ler e gravar dados para os fluxos de read(byte []) e write(byte []).
É isso aí.
Há, obviamente, os detalhes de implementação a considerar. Em primeiro lugar, você deve usar uma linha dedicada para todos os fluxos de leitura e escrita. Isto é importante porque tanto read(byte []) e write(byte []) são métodos de bloqueio de chamadas. read(byte []) irá bloquear até que haja algo para ler a partir do fluxo. write(byte []) não costuma bloquear, mas pode bloquear para controle de fluxo se o dispositivo remoto não estiver chamando read(byte []) com rapidez suficiente e se os buffers intermediários estiverem cheios. Assim, o loop principal na thread deve ser dedicado à leitura do InputStream. Um método público separado na linha pode ser usado para iniciar escrita pela OutputStream .
Exemplo
Aqui está um exemplo de como isso pode parecer:
[sourcecode language=”java”]
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Obter os fluxos de entrada e saída usando objetos temporaries
// porque os fluxos membros são finais
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) { }
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer = new byte[1024]; // buffer de memória para o fluxo
int bytes; // bytes returnados da read()
// Continuar obtendo o InputStream até ocorrer uma exceção
while (true) {
try {
// Leia a partir do InputStream
bytes = mmInStream.read(buffer);
// Envie os bytes obtidos com a atividade UI
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer) .sendToTarget();
} catch (IOException e) {
break;
}
}
}
/* Chamar isso da atividade principal para enviar dados para o dispositivo remoto */
public void write(byte[] bytes) {
try {
mmOutStream.write(bytes);
} catch (IOException e) { }
}
/* Chamar isso da atividade principal para o desligamento da conexão */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
[/sourcecode]
O construtor adquire os fluxos necessários e, uma vez executado, a thread esperará por dados que virão através do InputStream. Quando read(byte []) retorna com bytes do fluxo, os dados são enviados para a atividade principal usando um manipulador de membro da classe pai. Então ele irá voltar e aguardar mais bytes do fluxo.
Envio de dados de saída é simples; basta chamar a thread do método write() da atividade principal e passar os bytes a serem enviados. Este método, em seguida, simplesmente chama write(byte []) para enviar os dados para o dispositivo remoto.
A thread do método cancel() é importante para que a conexão possa ser rescindida a qualquer momento, mesmo fechando a BluetoothSocket . Este deve ser sempre chamado quando você terminar de usar a conexão Bluetooth.
Bom dia! Você poderia fornecer o projeto completo dessa aplicação? Eu estou com um pouco de dificuldades em compreender o seu código e acredito que com o projeto inteiro eu consiga entender melhor!
Desde já agradeço!
Ola, poderia ir colocando suas duvidas. Assim também podemos esclarecer. Estamos vendo o codigo para disponibilizar integral.
Bom, eu preciso desenvolver uma aplicação no android que se comunique com uma placa arduíno através do bluetooth.
Estou utilizando um módulo bluetooth acoplado ao arduíno para fazer essa comunicação. Acontece que o módulo bluetooth exige um Pin (1234) para que seja pareado. E até agora não encontrei em lugar nenhum alguma função no Android onde eu possa pesquisar os aparelhos bluetooth disponíveis e consiga digitar esse Pin para fazer a conexão. E também já tentei tirar esse Pin do módulo bluetooth e não consegui.
Se você conseguir me ajudar, agradeço muito!
Boa noite,
Muito show os códigos e as explicações, grande trabalho obrigado por compartilharem informação.
Bom dia, estive acompanhando o artigo, mas eu estava com a seguinte dúvida. É possível evitar que o aplicativo fique solicitando o PIN para o pareamento, uma vez que eu já sei a senha ?
Por exemplo: eu guardo a senha no banco de dados e quando me for solicitado eu inserir a senha que esta gravada no banco de dados, evitando assim que deixe de exibir a janela de Solicitação de Pareamento com o Bluetooth,
alguien me podria ayudar con mi proyecto ?
Qual a sua duvida?
Boa Tarde, estou acompanhando seu artigo e gostaria de saber se você irá dar continuidade. Estou utilizando o simulador OBDSim para realizar testes e gostaria de tirar algumas duvidas.
Olá amigo. Gostaria de saber se você irá continuar o projeto. Estou desenvolvendo uma aplicação e estou utilizando o simulador OBDSim para testes. Você ja fez algo parecido?
Att.
Olá amigo você irá continuar o projeto? Estou desenvolvendo uma aplicação e utilizando para testes o emulador OBDSim. Você já fez algo parecido?
Att.
Olá amigo você irá continuar o projeto? Estou desenvolvendo uma aplicação e utilizando para testes o emulador OBDSim. Você já fez algo parecido?
Att.
bom dia tem como me enviar o fonte completo para o meu e-mail.