前段时间公司一直在做与蓝牙通讯的项目,蓝牙在网络上有很多资料,发现有些地方还是有些讲述不具体的地方,所以决定总结一下。蓝牙低功耗通讯的工具类使用方法。
添加蓝牙权限
<!-- 添加蓝牙权限-->
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
//以下的使用是让其只是在蓝牙低功耗中有效。
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
概念一:因为低功耗蓝牙是从苹果那边继承沿用的,部分硬件上的描述可能会让安卓工程师懵比,苹果在打印特征中只是打印不同的部分,但是安卓确是打印整体。例如苹果中的特征是“FFE1”,在安卓中表示应该是“0000FFE1-0000-1000-8000-00805f9b34fb”。
概念二: 蓝牙通讯之中最重要的就是uuid,因为其他的信息都可以通过扫描之后获得。唯有uuid是需要开发者填写进去的交流的双方约定唯一的“钥匙”。uuid可以理解为端口,基本上分为三种,发送数据的uuid,接收数据的uuid,与功能描述的uuid。在实际的开发中间,有一些蓝牙的厂商,会将发送数据的端口与接收数据的端口使用同一个,那样子就是发送跟接受基本上是同一个uuid,至于功能描述的uuid根据所需的不同的功能加入。而发送数据描述uuid为"00002902-0000-1000-8000-00805f9b34fb"。
开发蓝牙的流程工具类的流程网络上很多教程:
蓝牙初始化——判断蓝牙是否打开(如果没有打开就打开蓝牙)——扫描设备——建立连接——蓝牙通讯
以下贴上蓝牙工具类代码
package Util;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.util.Log;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import MyEventBus.QueryInstructionsEventbus;
import de.greenrobot.event.EventBus;
/**
* Created by Administrator on 2017/4/25.
* 低功耗蓝牙的工具类
*/
public class BleUtil {
private static final String TAG = "BleUtil";
private static final long SCAN_PERIOD = 2000;
public static String characterUUID1 = "0000ffe1-0000-1000-8000-00805f9b34fb";//APP发送命令
public static String characterUUID2 = "0000ffe1-0000-1000-8000-00805f9b34fb";//BLE用于回复命令
private static String descriptorUUID = "00002902-0000-1000-8000-00805f9b34fb";//BLE设备特性的UUID
public static byte[] workModel = {0x02, 0x01};
private Context mContext;
private static BleUtil mInstance;
//作为中央来使用和处理数据;
private BluetoothGatt mGatt;
private BluetoothManager manager;
private BTUtilListener mListener;
private BluetoothDevice mCurDevice;
private BluetoothAdapter mBtAdapter;
private List<BluetoothDevice> listDevice;
private List<BluetoothGattService> serviceList;//服务
private List<BluetoothGattCharacteristic> characterList;//特征
private BluetoothGattService service;
public static BluetoothGattCharacteristic character1;
private BluetoothGattCharacteristic character2;
public static String Connnet_Bule_name = "";
public static synchronized BleUtil getInstance() {
if (mInstance == null) {
mInstance = new BleUtil();
}
return mInstance;
}
public void setContext(Context context) {
mContext = context;
init();
}
public void init() {
listDevice = new ArrayList<>();
if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
showToast("BLE不支持此设备!");
((Activity) mContext).finish();
}
manager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
//注:这里通过getSystemService获取BluetoothManager,
//再通过BluetoothManager获取BluetoothAdapter。BluetoothManager在Android4.3以上支持(API level 18)。
if (manager != null) {
mBtAdapter = manager.getAdapter();
}
if (mBtAdapter == null || !mBtAdapter.isEnabled()) {
mBtAdapter.enable();
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
enableBtIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(enableBtIntent);
}
}
//扫描设备的回调
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi,
byte[] scanRecord) {
try {
Log.e("onLeScan", device.getName());
if (!listDevice.contains(device)) {
//不重复添加
listDevice.add(device);
Log.e(TAG, "device:" + device.toString());
}
mListener.onLeScanDevices(listDevice);
} catch (Exception e) {
Log.e("onLeScan", e.getMessage());
}
}
};
//扫描设备
public void scanLeDevice(final boolean enable) {
if (enable) {
Handler mHandler = new Handler();
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
stopScan();
Log.e(TAG, "run: stop");
}
}, SCAN_PERIOD);
startScan();
Log.e(TAG, "start");
} else {
stopScan();
Log.e(TAG, "stop");
}
}
//开始扫描BLE设备
private void startScan() {
mBtAdapter.startLeScan(mLeScanCallback);
mListener.onLeScanStart();
}
//停止扫描BLE设备
private void stopScan() {
mBtAdapter.stopLeScan(mLeScanCallback);
mListener.onLeScanStop();
}
//返回中央的状态和周边提供的数据
private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
try {
Log.e(TAG, "onConnectionStateChange");
switch (newState) {
case BluetoothProfile.STATE_CONNECTED:
Log.e(TAG, "STATE_CONNECTED");
gatt.discoverServices(); //搜索连接设备所支持的service
Connnet_Bule_name = mCurDevice.getName();
mListener.onConnected(mCurDevice);
break;
case BluetoothProfile.STATE_DISCONNECTED:
disConnGatt();
Log.e(TAG, "STATE_DISCONNECTED");
Connnet_Bule_name = "";
character1 = null;
mListener.onDisConnected(mCurDevice);
break;
case BluetoothProfile.STATE_CONNECTING:
Log.e(TAG, "STATE_CONNECTING");
mListener.onConnecting(mCurDevice);
break;
case BluetoothProfile.STATE_DISCONNECTING:
Log.e(TAG, "STATE_DISCONNECTING");
mListener.onDisConnecting(mCurDevice);
break;
}
} catch (Exception e) {
}
super.onConnectionStateChange(gatt, status, newState);
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
Log.d(TAG, "onServicesDiscovered");
if (status == BluetoothGatt.GATT_SUCCESS) {
serviceList = gatt.getServices();
for (int i = 0; i < serviceList.size(); i++) {
BluetoothGattService theService = serviceList.get(i);
Log.e(TAG, "ServiceName:" + theService.getUuid());
characterList = theService.getCharacteristics();
for (int j = 0; j < characterList.size(); j++) {
String uuid = characterList.get(j).getUuid().toString();
Log.e(TAG, "---CharacterName:" + uuid);
if (uuid.equals(characterUUID1)) {
character1 = characterList.get(j);
character2 = characterList.get(j);
} else if (uuid.equals(characterUUID2)) {
}
}
}
setNotification();
}
super.onServicesDiscovered(gatt, status);
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
Log.e(TAG, "onCharacteristicRead " + gatt.getDevice().getName()
+ " Read "
+ characteristic.getUuid().toString()
+ " -> "
+ "结果是:" + characteristic.getValue());
super.onCharacteristicRead(gatt, characteristic, status);
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
Log.e(TAG, "onCharWrite " + gatt.getDevice().getName()
+ " write "
+ characteristic.getUuid().toString()
+ " -> "
+ "结果是:" + characteristic.getValue());
super.onCharacteristicWrite(gatt, characteristic, status);
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
Log.e(TAG, "onCharacteristicChanged");
// 这里是可以监听到设备自身或者手机改变设备的一些数据修改h通知
receiveData(characteristic);
super.onCharacteristicChanged(gatt, characteristic);
}
};
//获取设备指定的特征中的特性,其中对其进行监听, setCharacteristicNotification与上面的回调onCharacteristicChanged进行一一搭配
private void setNotification() {
mGatt.setCharacteristicNotification(character2, true);
BluetoothGattDescriptor descriptor = character2.getDescriptor(UUID.fromString(descriptorUUID));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mGatt.writeDescriptor(descriptor);
}
//接收数据,对其进行处理(这里才是数据的回传处理)
private void receiveData(BluetoothGattCharacteristic ch) {
byte[] bytes = ch.getValue();
//查询指令帧的回传
if ((bytes[0] & 0xff) == 186) {
if ((bytes[1] & 0xff) == 2) {
instructions.model = bytes[2] & 0xff;
instructions.brightness = bytes[3] & 0xff;
instructions.Tone = bytes[4] & 0xff;
instructions.Timing_state = bytes[5] & 0xff;
instructions.Time = bytes[6] & 0xff;
EventBus.getDefault().post(new QueryInstructionsEventbus());
}
}
}
//连接设备
public void connectLeDevice(int devicePos) {
mBtAdapter.stopLeScan(mLeScanCallback);
mCurDevice = listDevice.get(devicePos);
new Handler().postDelayed(new Runnable() {//延时连接,防止蓝牙服务还未绑定起来,就连接,导致异常
@Override
public void run() {
checkConnGatt();
}
}, 500);
}
//连接设备
public void connectLeDevice(BluetoothDevice devicePos) {
mBtAdapter.stopLeScan(mLeScanCallback);
mCurDevice = devicePos;
new Handler().postDelayed(new Runnable() {//延时连接,防止蓝牙服务还未绑定起来,就连接,导致异常
@Override
public void run() {
checkConnGatt();
}
}, 500);
}
//发送进入工作模式请求
public void sendWorkModel() {
if (character1 != null) {
character1.setValue(workModel);
mGatt.writeCharacteristic(character1);
}
}
//发送数据
public void SendDateBybyte(byte[] date) {
if (character1 != null) {
character1.setValue(date);
mGatt.writeCharacteristic(character1);
}
}
//发送强度
public void sendStrength(int strength) {
byte[] strengthModel = {0x01, (byte) strength};
if (character1 != null) {
character1.setValue(strengthModel);
mGatt.writeCharacteristic(character1);
}
}
//检查设备是否连接了
private void checkConnGatt() {
if (mGatt == null) {
mGatt = mCurDevice.connectGatt(mContext, true, mGattCallback);
try {
mListener.onConnecting(mCurDevice);
} catch (Exception e) {
}
} else {
mGatt.connect();
mGatt.discoverServices();
}
}
// 断开设备连接
private void disConnGatt() {
if (mGatt != null) {
mGatt.disconnect();
mGatt.close();
mGatt = null;
listDevice = new ArrayList<>();
mListener.onLeScanDevices(listDevice);
}
}
private void showToast(String message) {
Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
}
public void setBTUtilListener(BTUtilListener listener) {
mListener = listener;
}
public interface BTUtilListener {
void onLeScanStart(); // 扫描开始
void onLeScanStop(); // 扫描停止
void onLeScanDevices(List<BluetoothDevice> listDevice); //扫描得到的设备
void onConnected(BluetoothDevice mCurDevice); //设备的连接
void onDisConnected(BluetoothDevice mCurDevice); //设备断开连接
void onConnecting(BluetoothDevice mCurDevice); //设备连接中
void onDisConnecting(BluetoothDevice mCurDevice); //设备连接失败
void onStrength(int strength); //给设备设置强度
void onModel(int model); //设备模式
}
}
以上的蓝牙工具类中注意的点:
1:我增加了发送数据的方法。
//发送数据
public void SendDateBybyte(byte[] date) {
if (character1 != null) {
character1.setValue(date);
mGatt.writeCharacteristic(character1);
}
}
2:经过测试蓝牙的回传数据不是在onCharacteristicRead方法中回调,而是在receiveData方法中回调。
以上的工具类经过测试有效,但是无意之中发现了有谷歌推荐的官方的工具类可以使用。教程地址为:http://blog.csdn.net/will4906/article/details/53042382locationNum=4&fps=1
有兴趣的话可以去试试看,本人因为接触蓝牙开发时间一共两个星期,现已经跳槽,等他日有空再来细细回味。