一个Android平台上蓝牙串口通信辅助类的实现
蓝牙是Android平台上经常使用的功能,通过蓝牙通信都需要经过设备查找,设备配对,建立通信链路等过程。为避免在不同的应用中重复写相同的代码,我们可以将蓝牙通信中通用的代码封装起来,形成一套通用的类库,供不同的应用使用,使得应用可以只关注于本身的逻辑实现,而不需要更多关注通信的细节。
蓝牙通信的原理
蓝牙技术规定每一对设备之间进行蓝牙通讯时,必须一个为主角色,另一为从角色,才能进行通信,通信时,必须由主端进行查找,发起配对,建链成功后,双方即可收发数据。
蓝牙主端设备发起呼叫,首先是查找,找出周围处于可被查找的蓝牙设备。主端设备找到从端蓝牙设备后,与从端蓝牙设备进行配对,此时需要输入从端设备的PIN码,也有设备不需要输入PIN码。
配对完成后,从端蓝牙设备会记录主端设备的信任信息,此时主端即可向从端设备发起呼叫,已配对的设备在下次呼叫时,不再需要重新配对。
蓝牙通信类的实现方法
在通信中,我们把主端成为客户端,从端称为服务端,针对服务端和客户端操作的不同,为客户端和服务端创建不同的类,类图如下所示:
BluetoothTransport:为抽取的蓝牙设备的通用功能类,用于本机蓝牙设备开启、关闭以及蓝牙设备信息查询功能。
BluetoothClient:类为蓝牙通信客户端类,完成设备查找,配对,发送链接请求创建链接的功能。
BluetoothServer :类蓝牙通信设备服务端类,完成设备可查找,接收链接请求建立链接的功能。
Tansport:为设备类接口;
TransportClient:为客户端类接口;
TransportServer:为服务端类接口;
各类代码实现如下:
Tranport:
import android.app.Activity;
public interface Transport {
String getDeviceName();
boolean isHaveTransport();
boolean isTransportOpened();
void open(Activity activity, int requestId);
void close();
}
TransportClient:
import java.io.InputStream;
import java.io.OutputStream;
public interface TransportClient extends Transport {
interface OnClientListener {
void onConnected(InputStream inputStream, OutputStream outputStream);
void onConnectionFailed();
void onDisconnected();
void onClose();
}
interface OnDiscoveryListener {
void onDiscover(String deviceName, String deviceAddress);
void onFinish();
}
OnClientListener getClientListener();
void setClientListener(OnClientListener mClientListener);
OnDiscoveryListener getDiscoveryListener();
void setDiscoveryListener(OnDiscoveryListener mDiscoveryListener);
void startDiscovery(int delay);
void cancelDiscovery();
void startClient(String deviceName, String deviceAddress, String serverUUID);
void stopClient();
}
TransportServer:
import java.io.InputStream;
import java.io.OutputStream;
public interface TransportServer extends Transport {
interface OnServerListener {
void onConnected(InputStream inputStream, OutputStream outputStream);
void onDisconnected();
void onClose();
}
OnServerListener getServerListener();
void setServerListener(OnServerListener mServerListener);
void setDiscoverable(int discoverId);
void startServer(String serverName, String serverUUID);
void stopServer();
}
BluetoothTransport
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.content.Intent;
public class BluetoothTransport implements Transport {
protected BluetoothAdapter mBluetoothAdapter;
protected boolean bOpenedLocal;
protected Activity activity;
public BluetoothTransport() {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
bOpenedLocal = false;
}
@Override
public String getDeviceName() {
if (mBluetoothAdapter != null) {
return mBluetoothAdapter.getName();
} else {
return "";
}
}
@Override
public boolean isHaveTransport() {
return mBluetoothAdapter != null;
}
@Override
public boolean isTransportOpened() {
if (mBluetoothAdapter != null) {
return mBluetoothAdapter.isEnabled();
} else {
return false;
}
}
@Override
public void open(Activity activity, int requestId) {
//弹出对话框提示用户是后打开
Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
activity.startActivityForResult(enabler, requestId);
//不做提示,直接打开,不建议用下面的方法,有的手机会有问题。
// mBluetoothAdapter.enable();
bOpenedLocal = true;
this.activity = activity;
}
@Override
public void close() {
if (bOpenedLocal) {
if (mBluetoothAdapter != null) {
mBluetoothAdapter.disable();
}
}
}
}
BluetoothClient
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class BluetoothClient extends BluetoothTransport implements TransportClient {
private OnClientListener mClientListener = null;
private OnDiscoveryListener mDiscoveryListener = null;
private boolean bRegisterReceiver = false;
private ScheduledExecutorService startScheduledExecutor;
private Set<BluetoothDevice> deviceVector = new HashSet<>();
private BluetoothSocket mClientSocket = null;
public BluetoothClient() {
super();
startScheduledExecutor = Executors.newSingleThreadScheduledExecutor();
}
@Override
public OnClientListener getClientListener() {
return mClientListener;
}
@Override
public void setClientListener(OnClientListener clientListener) {
mClientListener = clientListener;
}
@Override
public OnDiscoveryListener getDiscoveryListener() {
return mDiscoveryListener;
}
@Override
public void setDiscoveryListener(OnDiscoveryListener discoveryListener) {
mDiscoveryListener = discoveryListener;
}
private void scanBluetoothDevices(){
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
for (BluetoothDevice btDev : pairedDevices) {
deviceVector.add(btDev);
mDiscoveryListener.onDiscover(btDev.getName(), btDev.getAddress());
}
}
}
private BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver(){
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
BluetoothDevice scanDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if(BluetoothDevice.ACTION_FOUND.equals(action)){//每扫描到一个设备,系统都会发送此广播。
//获取蓝牙设备
if(scanDevice == null || scanDevice.getName() == null) return;
if (mDiscoveryListener != null) {
switch (scanDevice.getBondState()) {
case BluetoothDevice.BOND_BONDED:
break;
case BluetoothDevice.BOND_BONDING:
break;
case BluetoothDevice.BOND_NONE:
break;
}
//scanDevice.createBond();
deviceVector.add(scanDevice);
mDiscoveryListener.onDiscover(scanDevice.getName(), scanDevice.getAddress());
}
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
if (mDiscoveryListener != null) {
mDiscoveryListener.onFinish();
}
cancelDiscovery();
} else if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
switch (device.getBondState()) {
case BluetoothDevice.BOND_BONDING:
// Log.d("BlueToothTestActivity", "正在配对......");
break;
case BluetoothDevice.BOND_BONDED:
// Log.d("BlueToothTestActivity", "完成配对");
break;
case BluetoothDevice.BOND_NONE:
// Log.d("BlueToothTestActivity", "取消配对");
break;
default:
break;
}
} else if(action.equals("android.bluetooth.device.action.PAIRING_REQUEST")) {
}
}
};
@Override
public void startDiscovery(int delaySecond) {
IntentFilter filter = new IntentFilter();
//发现设备
filter.addAction(BluetoothDevice.ACTION_FOUND);
//设备连接状态改变
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
//蓝牙设备状态改变
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
scanBluetoothDevices();
activity.registerReceiver(mBluetoothReceiver, filter);
bRegisterReceiver = true;
mBluetoothAdapter.startDiscovery();
startScheduledExecutor.schedule(new Runnable() {
@Override
public void run() {
cancelDiscovery();
}
}, delaySecond, TimeUnit.SECONDS);
}
@Override
public void cancelDiscovery() {
if (mBluetoothAdapter.isDiscovering()) {
mBluetoothAdapter.cancelDiscovery();
}
}
@Override
public void startClient(String deviceName, String deviceAddress, String serverUUID) {
BluetoothDevice deviceTmp = null;
for (BluetoothDevice bd : deviceVector) {
if (bd.getAddress().equals(deviceAddress)) {
deviceTmp = bd;
break;
}
}
if (deviceTmp == null) {
return;
}
final BluetoothDevice device = deviceTmp;
try {
mClientSocket = device.createRfcommSocketToServiceRecord(UUID.fromString(serverUUID));
} catch (IOException e) {
mClientSocket = null;
}
startScheduledExecutor.execute(new Runnable() {
private int mState = 0;
@Override
public void run() {
try {
// socket 连接,该调用会阻塞,直到连接成功或失败
mClientSocket.connect();
} catch (IOException e) {
try {
mClientSocket.close();
} catch (IOException ie) {
}
if (mClientListener != null) {
mClientListener.onConnectionFailed();
}
return;
}
// 启动连接线程
if (mClientListener != null) {
try {
mClientListener.onConnected(mClientSocket.getInputStream(), mClientSocket.getOutputStream());
} catch (IOException ex) {
mClientListener.onDisconnected();
}
}
}
});
}
@Override
public void stopClient() {
try {
if (mClientListener != null) {
mClientListener.onClose();
}
if (mClientSocket != null) {
mClientSocket.close();
}
} catch (IOException e) {
}
}
@Override
public void close() {
super.close();
if (bRegisterReceiver) {
activity.unregisterReceiver(mBluetoothReceiver);
bRegisterReceiver = false;
}
startScheduledExecutor.shutdown();
}
}
BluetoothServer
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
public class BluetoothServer extends BluetoothTransport implements TransportServer {
private OnServerListener mServerListener = null;
private BluetoothServerSocket mServerAcceptSocket = null;
private BluetoothSocket mServerSocket = null;
private boolean bServerRunning = false;
private ScheduledExecutorService startScheduledExecutor;
public BluetoothServer() {
super();
startScheduledExecutor = Executors.newSingleThreadScheduledExecutor();
}
@Override
public OnServerListener getServerListener() {
return mServerListener;
}
@Override
public void setServerListener(OnServerListener serverListener) {
mServerListener = serverListener;
}
@Override
public void setDiscoverable(int discoverId) {
if (mBluetoothAdapter.isEnabled()) {
if (mBluetoothAdapter.getScanMode() !=
BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
Intent discoverableIntent = new Intent(
BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(
BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120);
activity.startActivityForResult(discoverableIntent, discoverId);
}
}
}
@Override
public void startServer(String serverName, String serverUUID) {
try {
mServerAcceptSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(
serverName, UUID.fromString(serverUUID));
startScheduledExecutor.execute(new Runnable() {
@Override
public void run() {
bServerRunning = true;
// 循环,直到连接成功
while (bServerRunning) {
try {
// 这是一个阻塞调用 返回成功的连接
// mServerAcceptSocket.close()在另一个线程中调用,可以中止该阻塞
mServerSocket = mServerAcceptSocket.accept();
mServerAcceptSocket.close();
mServerAcceptSocket = null;
} catch (IOException e) {
break;
}
// 如果连接被接受
if (mServerSocket != null) {
if (mServerListener != null) {
try {
mServerListener.onConnected(mServerSocket.getInputStream(), mServerSocket.getOutputStream());
} catch (IOException ex) {
mServerListener.onDisconnected();
}
}
}
}
}
});
} catch (IOException e) {
}
}
@Override
public void stopServer() {
try {
if (mServerListener != null) {
mServerListener.onClose();
}
bServerRunning = false;
if (mServerAcceptSocket != null) {
mServerAcceptSocket.close();
}
if (mServerSocket != null) {
mServerSocket.close();
}
} catch (IOException e) {
// Log.e(TAG, "close() of server failed", e);
}
}
@Override
public void close() {
super.close();
startScheduledExecutor.shutdown();
}
}
使用方法
TransportClient和TransportServer需要在Activity的方法中使用
创建蓝牙客户端通信的代码框架如下所示:
BluetoothClient transportClient = new BluetoothClient();
final int REQUEST_ENABLE = 101;
protected void startClient() {
if (transportClient.isHaveTransport()) {
if (!transportClient.isTransportOpened()) {
transportClient.open(this, REQUEST_ENABLE);
}
}
transportClient.setClientListener(new TransportClient.OnClientListener() {
@Override
public void onConnected(InputStream inputStream, OutputStream outputStream) {
/*
* 通信代码
*/
}
@Override
public void onConnectionFailed() {
// 连接失败操作
}
@Override
public void onDisconnected() {
// 断开连接操作后的操作代码
}
@Override
public void onClose() {
// 通信关闭操作代码
}
});
transportClient.setDiscoveryListener(new TransportClient.OnDiscoveryListener() {
@Override
public void onDiscover(String deviceName, String deviceAddress) {
if (deviceName != null && deviceAddress != null) {
addDevice(deviceName, deviceAddress);
// deviceVector.add(device);
}
}
protected void addDevice(final String deviceName, final String deviceAddress) {
// 显示设备,供用户选择代码
// 用户选择设备后,调用 transportTookit startClient 方法
}
@Override
public void onFinish() {
// 发现设备结束
}
});
transportClient.startDiscovery(120);
}
创建蓝牙服务端通信的代码框架如下所示:
BluetoothServer transportServer = new BluetoothServer();
String serverName = ""; //服务名
String serverUUID = ""; //服务UUID
final int REQUEST_DISCOVERABLE = 102;
void startServer() {
if (transportServer.isHaveTransport()) {
if (!transportServer.isTransportOpened()) {
transportServer.open(this, REQUEST_ENABLE);
}
}
transportServer.setServerListener(new TransportServer.OnServerListener() {
@Override
public void onConnected(InputStream inputStream, OutputStream outputStream) {
//通信代码
}
@Override
public void onDisconnected() {
// 链接断开代码
}
@Override
public void onClose() {
// 关闭代码
}
});
transportServer.setDiscoverable(REQUEST_DISCOVERABLE);
transportServer.startServer(serverName, serverUUID);
}