蓝牙设备连接及通信
1.设备连接,服务器端开启线程一直等待连接,客户端点击某个目标设备,关闭服务器线程监听,并开启线程,发出连接请求。
注意:客户端连接前,一定cancelDiscovery()
// 蓝牙已开启
if (bluetoothAdapter.isEnabled()) {
showBondDevice();
// 默认开启服务线程监听
if (serverThread != null) {
serverThread.cancel();
}
Log.e("tag", "-------------- new server thread");
serverThread = new ServerThread(bluetoothAdapter, uiHandler);
new Thread(serverThread).start();
}
resultList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
// 关闭服务器监听
if (serverThread != null) {
serverThread.cancel();
serverThread = null;
Log.e("TAg", "---------------client item click , cancel server thread ," +
"server thread is null");
}
BluetoothDevice device = deviceList.get(position);
// 开启客户端线程,连接点击的远程设备
clientThread = new ClientThread(bluetoothAdapter, device,uiHandler);
new Thread(clientThread).start();
// 通知 ui 连接的服务器端设备
Message message = new Message();
message.what = Params.MSG_CONNECT_TO_SERVER;
message.obj = device;
uiHandler.sendMessage(message);
}
});
2.服务端
android 蓝牙之间可以通过SDP协议建立连接进行通信,通信方式类似于平常使用socket。
首先创建BluetoothServerSocket ,BluetoothAdapter中提供了两种创建BluetoothServerSocket 方式,如下图所示为创建安全的RFCOMM Bluetooth socket,该连接是安全的需要进行配对。而通过listenUsingInsecureRfcommWithServiceRecord创建的RFCOMM Bluetooth socket是不安全的,连接时不需要进行配对。
其中的uuid需要服务器端和客户端进行统一。
package com.example.zb.mybluetoothdemo;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;
/**
* Created by zb on 2019/5/6.
*/
public class ServerThread implements Runnable{
final String TAG = "ServerThread";
BluetoothAdapter bluetoothAdapter;
// 本地服务器套接字
BluetoothServerSocket serverSocket =null;
BluetoothSocket socket = null;
Handler uiHandler;
OutputStream out;
InputStream in;
boolean acceptFlag = true;
public ServerThread(BluetoothAdapter bluetoothAdapter, Handler handler) {
this.bluetoothAdapter = bluetoothAdapter;
this.uiHandler = handler;
BluetoothServerSocket tmp = null;
try {
// 创建一个新的监听服务器套接字
tmp = bluetoothAdapter.listenUsingRfcommWithServiceRecord(Params.NAME, UUID.fromString(Params.UUID));
} catch (IOException e) {
e.printStackTrace();
}
serverSocket = tmp;
Log.e(TAG, "-------------- do new()");
}
@Override
public void run() {
Log.e(TAG, "-------------- do run()");
try {
while (acceptFlag) {
// 这是一个阻塞调用 返回成功的连接
// mServerSocket.close()在另一个线程中调用,可以中止该阻塞
socket = serverSocket.accept();
// 阻塞,直到有客户端连接
if (socket != null) {
Log.e(TAG, "-------------- socket not null, get a client");
out = socket.getOutputStream();
in = socket.getInputStream();
BluetoothDevice remoteDevice = socket.getRemoteDevice();
Message message = new Message();
message.what = Params.MSG_REV_A_CLIENT;
message.obj = remoteDevice;
uiHandler.sendMessage(message);
// 读取服务器 socket 数据
new Thread(new Runnable() {
@Override
public void run() {
Log.e(TAG, "-----------do server read run()");
byte[] buffer = new byte[1024];
int len;
String content;
try {
while ((len = in.read(buffer)) != -1) {
content = new String(buffer, 0, len);
Message message = new Message();
message.what = Params.MSG_CLIENT_REV_NEW;
message.obj = content;
uiHandler.sendMessage(message);
Log.e(TAG, "------------- server read data in while ,send msg ui" + content);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void write(String data){
try {
out.write(data.getBytes("utf-8"));
Log.e(TAG, "---------- write data ok "+data);
} catch (IOException e) {
e.printStackTrace();
}
}
public void cancel() {
try {
acceptFlag = false;
serverSocket.close();
Log.e(TAG, "-------------- do cancel ,flag is "+acceptFlag);
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "----------------- cancel " + TAG + " error");
}
}
}
3.客户端
客户端主要用来创建RFCOMM socket,并连接服务端。
先扫描周围的蓝牙设备,如果扫描到指定设备则进行连接。mBlthChatUtil.connect(scanDevice)连接到设备,
连接过程主要在ConnectThread线程中进行,先创建socket,方式有两种,
如下代码中是安全的(createRfcommSocketToServiceRecord)。另一种不安全连接对应的函数是createInsecureRfcommSocketToServiceRecord。
package com.example.zb.mybluetoothdemo;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;
/**
* Created by zb on 2019/5/6.
*/
public class ClientThread implements Runnable{
final String TAG = "ClientThread";
BluetoothAdapter bluetoothAdapter;
BluetoothDevice device;
Handler uiHandler;
BluetoothSocket socket;
OutputStream out;
InputStream in;
public ClientThread(BluetoothAdapter bluetoothAdapter, BluetoothDevice device, Handler handler) {
this.bluetoothAdapter = bluetoothAdapter;
this.device = device;
this.uiHandler = handler;
BluetoothSocket tmp = null;
try {
//创建socket
tmp = device.createRfcommSocketToServiceRecord(UUID.fromString(Params.UUID));
} catch (IOException e) {
e.printStackTrace();
}
socket = tmp;
}
@Override
public void run() {
Log.e(TAG, "----------------- do client thread run()");
if (bluetoothAdapter.isDiscovering()) {
bluetoothAdapter.cancelDiscovery();
}
try {
// socket 连接,该调用会阻塞,直到连接成功或失败
socket.connect();
out = socket.getOutputStream();
in = socket.getInputStream();
new Thread(new Runnable() {
@Override
public void run() {
Log.e(TAG, "-----------do client read run()");
byte[] buffer = new byte[1024];
int len;
String content;
try {
//读数据
while ((len=in.read(buffer)) != -1) {
content=new String(buffer, 0, len);
Message message = new Message();
message.what = Params.MSG_CLIENT_REV_NEW;
message.obj = content;
//更新 ui
uiHandler.sendMessage(message);
Log.e(TAG, "------------- client read data in while ,send msg ui" + content);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "-------------- exception");
}
}
//写数据
public void write(String data){
try {
out.write(data.getBytes("utf-8"));
Log.e(TAG, "---------- write data ok "+data);
} catch (IOException e) {
e.printStackTrace();
}
}
}
4.通信
/**
* 向 socket 写入发送的数据
*
* @param dataSend
*/
public void writeData(String dataSend) {
if (serverThread != null) {
serverThread.write(dataSend);
} else if (clientThread != null) {
clientThread.write(dataSend);
}
}
(一)打开/关闭蓝牙设备:https://blog.csdn.net/lumingzhang/article/details/89922173
(二)搜索蓝牙设备并展示:https://blog.csdn.net/lumingzhang/article/details/89922235
本人github地址:https://github.com/b888zhang/MyBluetoothDemo
本文在原文上修改,原文地址:https://blog.csdn.net/qiao_jim/article/details/73008695