Android蓝牙中涉及通用数据传输协议的有两种:
SPP协议是Android 2.0引入的API,是通过Socket的形式来实现数据传输及交互,有分客户端和服务端,手机一般以客户端的角色主动连接SPP协议设备。
BLE协议是Android 4.3引入的API,但手机厂商大部份在Android 4.4上才支持BLE,即低功耗蓝牙,一般我们开发的话是使用中央(BluetoothGatt)或者外围(BluetoothGattServer)来进行开发的,手机正常情况下当作中央设备来接收信息,而蓝牙模块当作是外围设备发送数据。
SPP协议的连接流程:
SPP协议涉及4个类:
获取本地蓝牙适配器:
public static BluetoothAdapter getBtAdapter() {
Context mContext = CSmartApplication.getInstance().getApplicationContext();
BluetoothManager bluetoothManager = (BluetoothManager) mContext
.getSystemService(Context.BLUETOOTH_SERVICE);
return bluetoothManager.getAdapter();
}
注册监听:
private void requestProfileConnectionState() {
//检测连接状态:
int a2dp = mBluetoothAdapter.getProfileConnectionState(BluetoothProfile.A2DP);
int gatt = mBluetoothAdapter.getProfileConnectionState(BluetoothProfile.GATT);
int sap = mBluetoothAdapter.getProfileConnectionState(BluetoothProfile.SAP);
//据是否有连接获取已连接的设备
int flag = -1;
if (a2dp == BluetoothProfile.STATE_CONNECTED) {
flag = a2dp;
} else if (gatt == BluetoothProfile.STATE_CONNECTED) {
flag = gatt;
} else if (sap == BluetoothProfile.STATE_CONNECTED) {
flag = sap;
}
if (flag != -1) {
ProxyListener mProxyListener = new ProxyListener();
mBluetoothAdapter.getProfileProxy(this, mProxyListener, flag);
}
}
private void listenBlueState() {
//检查当前是否存在蓝牙连接
requestProfileConnectionState();
if (mFondDevieReceiver == null) {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
mFondDevieReceiver = new FoundDeviceReceiver();
registerReceiver(mFondDevieReceiver, intentFilter);
}
}
获取远程蓝牙设备:
private class ProxyListener implements BluetoothProfile.ServiceListener {
@Override
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (proxy != null) {
List mDevices = proxy.getConnectedDevices();
if (mDevices.size() > 0) {
for (int i = 0; i < mDevices.size(); i++) {
//获取BluetoothDevice
mDevice = mDevices.get(0);
logShow("mDevice: " + mDevices.get(0).getAddress());
//调用创建Socket连接
connectDevice();
}
}
mBluetoothAdapter.closeProfileProxy(profile, proxy);
}
}
@Override
public void onServiceDisconnected(int profile) {
}
}
private class FoundDeviceReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action)
|| BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
int a2dpState = intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, -1);
int adapterState = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, BluetoothAdapter.ERROR);
if (BluetoothA2dp.STATE_CONNECTED == a2dpState || BluetoothAdapter.STATE_CONNECTED == adapterState) {//连接成功
//获取BluetoothDevice
mDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
logShow("STATE_CONNECTED: " + mDevice.getAddress());
//调用创建Socket连接
connectDevice();
} else if (BluetoothA2dp.STATE_CONNECTING == a2dpState) {//正在连接
} else if (BluetoothA2dp.STATE_DISCONNECTED == a2dpState) {//取消连接
logShow("STATE_DISCONNECTED");
......
}
}
if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
BluetoothAdapter.ERROR);
switch (state) {
case BluetoothAdapter.STATE_OFF:
break;
case BluetoothAdapter.STATE_TURNING_OFF:
break;
case BluetoothAdapter.STATE_ON:
break;
case BluetoothAdapter.STATE_TURNING_ON:
break;
}
}
}
}
创建Socket:
创建RFCOMM的UUID需要与设备端约定,一般使用默认的就可以,但有时因项目特殊,也会另有定义。
private static final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");//默认UUID
/**
* Create the RFCOMM bluetooth socket.
*
* @param uuid UUID to create the socket with.
* @return BluetoothSocket object.
*/
@TargetApi(10)
private BluetoothSocket createSocket(UUID uuid) {
BluetoothSocket socket = null;
try {
if (VERSION.SDK_INT >= 10) {
socket = mBTDevice.createInsecureRfcommSocketToServiceRecord(uuid);
} else {
socket = mBTDevice.createRfcommSocketToServiceRecord(uuid);
}
} catch (IOException e) {
if (mDebug)
Log.e(TAG, "createSocket: " + e.toString());
try {
Method method = mBTDevice.getClass().getMethod("createRfcommSocket", new Class[]{int.class});
socket = (BluetoothSocket) method.invoke(mBTDevice, Integer.valueOf(1));
return socket;
} catch (Exception e1) {
if (mDebug)
handleException("createSocket", TypeException.CONNECTION_FAILED, e1);
}
}
return socket;
}
数据读写:
注:mInputStream.read(buffer)是I/O口堵塞式的
mBTSocket.connect();
mInputStream = mBTSocket.getInputStream();
//启动新线程去处理
while (this.going) {
if (mInputStream != null) {
try {
int bytes = mInputStream.read(buffer);
if (bytes < 0) {
this.going = false;
} else {
Log.e(TAG, "buffer: " + Arrays.toString(buffer));
//数据解析
mHandler.obtainMessage(Message.MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
}
} catch (Exception e) {
Log.e(TAG, "mInputStream read is null");
this.going = false;
}
}
}
public void sendData(byte[] data) {
if (!this.mDfuBusy) {//判断连接是否有效
if (mBTSocket == null) {
Log.e(TAG, "sendCommandData: not connected.");
return;
}
try {
OutputStream outputStream = mBTSocket.getOutputStream();
if (outputStream != null) {
try {
outputStream.write(data);
} catch (Exception e) {
Log.e(TAG, "outputStream.write null");
}
} else {
Log.e(TAG, "outputStream is null");
}
} catch (IOException e) {
Log.e(TAG, "sendData: " + e);
}
}
}
接口关闭:
private void disconnectBluetooth() {
if (mDebug) {
Log.i(TAG, "disconnect BT");
}
if (mBTSocket != null) {
try {
if (mInputStream != null) {
mInputStream.close();
mInputStream = null;
}
if (mBTSocket != null) {
if (mBTSocket.getInputStream() != null) {
mBTSocket.getInputStream().close();
}
}
if (mBTSocket != null) {
if (mBTSocket.getOutputStream() != null) {
mBTSocket.getOutputStream().close();
}
}
if (mBTSocket != null) {
mBTSocket.close();
}
mBTSocket = null;
mBTDevice = null;
} catch (IOException e) {
if (mDebug) {
Log.e(TAG, e.toString());
}
}
}
}
服务端的Socket建立与客户端差不多,需注意以下两个步骤:
总之:SPP的连接和操作相对比较简单,考虑的事情也少。需要注意的是频繁数据发送(间隔时间短)的情况下,APP收到的数据并不是按原先约定的TLV一包一包数据,可能会被拆包,应对的办法就是启动线程接收完数据,然后去一包一包取。