这篇博客帮助用来帮助我们在App开发中需要连接蓝牙硬件设备接收与上传数据的,如果大家有什么不理解的或者更好的方法欢迎大家与我沟通。在这里我就不介绍蓝牙的基本知识了,有不理解的大家可以找一找。一起来看下如何使用吧。
1.使用准备
1.在项目级的build.gradle文件中加入aar文件。
implementation(name: 'Sbbluetooth', ext: "aar")
2.在清单文件中添加蓝牙权限
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/><uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" tools:ignore="MockLocation,ProtectedPermissions" /> <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" tools:ignore="ProtectedPermissions" /> <uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
2.开始使用
1.在自定义的Application的onCreate里面初始化
SBluetoothContext.set(this);
2.使用
1.创建ClientManager类,单例管理。
public class ClientManager {
private static SBluetoothClient mClient;
public static SBluetoothClient getClient() {
if (mClient == null) {
synchronized (ClientManager.class) {
if (mClient == null) {
mClient = new SBluetoothClient(AppAplication.getInstance());
}
}
}
return mClient;
}
}
2.在需要扫描蓝牙设备的地方使用,使用之前先判断蓝牙是否开启,如果没有开启就打开蓝牙。
if (ClientManager.getClient().isBluetoothOpened()) {
ClientManager.getClient().openBluetooth();
}
3.搜索蓝牙设备并连接设备。
SearchRequest request = new SearchRequest.Builder()
.searchBluetoothLeDevice(3000, 3) // 扫BLE设备3次,每次3s
.searchBluetoothClassicDevice(5000) // 扫经典蓝牙5s
.searchBluetoothLeDevice(5000) // 扫BLE设备5s
.build();
ClientManager.getClient().search(request, searchResponse);
ClientManager.getClient().registerBluetoothBondListener(mBluetoothBondListener);
/**
* 搜索出的设备集合
*/
private List<Map<String, String>> devices = new ArrayList<>();
//这个类是查看搜索到的设备的
private SearchResponse searchResponse = new SearchResponse() {
@Override
public void onSearchStarted() {
}
@Override
public void onDeviceFounded(SearchResult device) {
RBroadcast RBroadcast = new RBroadcast(device.scanRecord);
// 定义一个装载蓝牙设备名字和地址的Map
Map<String, String> deviceMap = new HashMap<>();
// 过滤已配对的和重复的蓝牙设备
if (isSingleDevice(device.device)) {
deviceMap.put("name", device.getName() == null ? "null" : device.getName());
deviceMap.put("address", device.getAddress());
if (device.getName() != null && !device.getName().equals("NULL") )) {
devices.add(deviceMap);
// 把设备在列表中显示出来,
showDevices();
//如果这里知道已经找到了设备可以调用ClientManager.getClient().stopSearch();结束搜索。
}
}
// 显示发现的蓝牙设备列表,这里就是个recyclerview
mainRec.setVisibility(View.VISIBLE);
}
@Override
public void onSearchStopped() {
}
@Override
public void onSearchCanceled() {
}
};
/**
* 判断此设备是否存在
*/
private boolean isSingleDevice(BluetoothDevice device) {
if (devices == null) {
return true;
}
for (Map<String, String> mDeviceMap : devices) {
if ((device.getAddress()).equals(mDeviceMap.get("address"))) {
return false;
}
}
return true;
}
/**
* 监听设备配对状态变化
*/
private final BluetoothBondListener mBluetoothBondListener = new BluetoothBondListener() {
@Override
public void onBondStateChanged(String mac, int bondState) {
// bondState = Constants.BOND_NONE, BOND_BONDING, BOND_BONDED
if (bondState == BOND_BONDED) {
Log.e("onBondStateChanged: ", "设备配对成功");
} else if (bondState == BOND_BONDING) {
Log.e("onBondStateChanged: ", "设备配对中");
} else if (bondState == BOND_NONE) {
Log.e("onBondStateChanged: ", "没有设备配对");
Toast.makeText(ShouHuanActivity.this, "配对成功", Toast.LENGTH_LONG).show();
}
}
};
/**
* 显示搜索到的设备列表,这里就是用了个recyclerview自带的布局。你们根据自己的程序自己写
*/
private void showDevices() {
SimpleAdapter mSimpleAdapter = new SimpleAdapter(ShouHuanActivity.this, devices,
android.R.layout.simple_list_item_2,
new String[]{"name", "address"},
new int[]{android.R.id.text1, android.R.id.text2});
mainRec.setAdapter(mSimpleAdapter);
//这里是连接设备,根据设备的mac地址
ClientManager.getClient().connect(devices.get(posi).get("address"),new BleConnectResponse() {
@Override
public void onResponse(int code, BleGattProfile profile) {
if (code == REQUEST_SUCCESS) {
//连接成功,可以发送和接收数据了
notify();
} else if (code == Constants.REQUEST_FAILED) {
//连接失败
} else if (code == Constants.REQUEST_TIMEDOUT) {
//连接超时
}
}
});
ClientManager.getClient().registerConnectStatusListener(devices.get(posi).get("address"), mBleConnectStatusListener);
}
/**
* 监听蓝牙连接状态
*/
private final BleConnectStatusListener mBleConnectStatusListener = new BleConnectStatusListener() {
@Override
public void onConnectStatusChanged(String mac, int status) {
if (status == STATUS_CONNECTED) {
} else if (status == STATUS_DISCONNECTED) {
Log.e("onConnectStatusChanged: ", "断开连接");
}
}
};
4.发送和接收数据
private void notify(){
//通知接收数据
ClientManager.getClient().notify(“设备的mac地址”, UUID.fromString(Service), UUID.fromString(character), mNotifyRsp);
}
private final BleNotifyResponse mNotifyRsp = new BleNotifyResponse() {
@Override
public void onNotify(UUID service, UUID character, byte[] value) {
//在这里根据service和character判断当前收到的数据是否是你需要的,如果是就
//解析value里面的值就可以了,下面这个方法可以把值变成字符串形式
String s = String.format(ByteUtils.byteToString(value));
}
@Override
public void onResponse(int code) {
if (code == Constants.REQUEST_SUCCESS) {
Log.d("onResponse: ", "成功");
} else {
Log.d("onResponse: ", "失败");
}
}
};
//这个是给设备发送数据,需要把十六进制字符串转成字节数组发送,
//当设备收到数据并给手机回复数据时,会通过上面的mNotifyRsp把数据返回给手机,咱们可以在那里
//解析数据。要注意这里写的byte[]不能超过20字节,如果超过了需要自己分成几次写。建议的办法是第一个//byte放剩余要写的字节的长度。writeNoRsp方法比普通的write快2~3倍,建议用于固件升级。
ClientManager.getClient().write(mac, UUID.fromString(Service), UUID.fromString(character), HexStringToByteArray(s), new BleWriteResponse() {
@Override
public void onResponse(int code) {
Log.e("onResponse: ", "获取数据:" + code);
if (code == REQUEST_SUCCESS) {
Toast.makeText(ShouHuanActivity.this, "请求发送成功", Toast.LENGTH_SHORT).show();
} else if (code == REQUEST_FAILED) {
Toast.makeText(ShouHuanActivity.this, "请求发送失败", Toast.LENGTH_SHORT).show();
} else if (code == REQUEST_CANCELED) {
Toast.makeText(ShouHuanActivity.this, "请求发送取消", Toast.LENGTH_SHORT).show();
} else if (code == ILLEGAL_ARGUMENT) {
Toast.makeText(ShouHuanActivity.this, "请求参数非法", Toast.LENGTH_SHORT).show();
} else if (code == BLE_NOT_SUPPORTED) {
Toast.makeText(ShouHuanActivity.this, "蓝牙不支持", Toast.LENGTH_SHORT).show();
} else if (code == BLUETOOTH_DISABLED) {
Toast.makeText(ShouHuanActivity.this, "蓝牙未开启", Toast.LENGTH_SHORT).show();
} else if (code == SERVICE_UNREADY) {
Toast.makeText(ShouHuanActivity.this, "服务未开启", Toast.LENGTH_SHORT).show();
} else if (code == REQUEST_TIMEDOUT) {
Toast.makeText(ShouHuanActivity.this, "连接超时", Toast.LENGTH_SHORT).show();
} else if (code == REQUEST_DENIED) {
Toast.makeText(ShouHuanActivity.this, "请求被拒绝", Toast.LENGTH_SHORT).show();
} else if (code == REQUEST_EXCEPTION) {
Toast.makeText(ShouHuanActivity.this, "请求报错", Toast.LENGTH_SHORT).show();
} else if (code == REQUEST_UNKNOWN) {
Toast.makeText(ShouHuanActivity.this, "未知请求", Toast.LENGTH_SHORT).show();
}
}
});
/**
*
* @param hexString
* @return 将十六进制转换为字节数组
*/
public static byte[] HexStringToByteArray(String hexString){
//hexString的长度对2取整,作为bytes的长度
int len = hexString.length()/2;
byte[] bytes = new byte[len];
byte high = 0;//字节高四位
byte low = 0;//字节低四位
for(int i=0;i<len;i++){
//右移四位得到高位
high = (byte)((hexStr.indexOf(hexString.charAt(2*i)))<<4);
low = (byte)hexStr.indexOf(hexString.charAt(2*i+1));
bytes[i] = (byte) (high|low);//高地位做或运算
}
return bytes;
}
//当设备提供read操作时可用此方法直接获取返回值
ClientManager.getClient().read(mac, UUID.fromString(Service), UUID.fromString(character), new BleReadResponse() {
/**
* @param code
* @param data
*/
@Override
public void onResponse(int code, byte[] data) {
if (code == REQUEST_SUCCESS) {
Toast.makeText(ShouHuanActivity.this, "请求发送成功", Toast.LENGTH_SHORT).show();
} else if (code == REQUEST_FAILED) {
Toast.makeText(ShouHuanActivity.this, "请求发送失败", Toast.LENGTH_SHORT).show();
} else if (code == REQUEST_CANCELED) {
Toast.makeText(ShouHuanActivity.this, "请求发送取消", Toast.LENGTH_SHORT).show();
} else if (code == ILLEGAL_ARGUMENT) {
Toast.makeText(ShouHuanActivity.this, "请求参数非法", Toast.LENGTH_SHORT).show();
} else if (code == BLE_NOT_SUPPORTED) {
Toast.makeText(ShouHuanActivity.this, "蓝牙不支持", Toast.LENGTH_SHORT).show();
} else if (code == BLUETOOTH_DISABLED) {
Toast.makeText(ShouHuanActivity.this, "蓝牙未开启", Toast.LENGTH_SHORT).show();
} else if (code == SERVICE_UNREADY) {
Toast.makeText(ShouHuanActivity.this, "服务未开启", Toast.LENGTH_SHORT).show();
} else if (code == REQUEST_TIMEDOUT) {
Toast.makeText(ShouHuanActivity.this, "连接超时", Toast.LENGTH_SHORT).show();
} else if (code == REQUEST_DENIED) {
Toast.makeText(ShouHuanActivity.this, "请求被拒绝", Toast.LENGTH_SHORT).show();
} else if (code == REQUEST_EXCEPTION) {
Toast.makeText(ShouHuanActivity.this, "请求报错", Toast.LENGTH_SHORT).show();
} else if (code == REQUEST_UNKNOWN) {
Toast.makeText(ShouHuanActivity.this, "未知请求", Toast.LENGTH_SHORT).show();
}
String s = Utils.byte2hex(data);
}
});
/**
* 字节数组转换为十六进制字符串
* (非常重要)
*
* @param b
* byte[] 需要转换的字节数组
* @return String 十六进制字符串
*/
public static final String byte2hex(byte b[]) {
if (b == null) {
throw new IllegalArgumentException(
"Argument b ( byte array ) is null! ");
}
String hs = "";
String stmp = "";
for (int n = 0; n < b.length; n++) {
stmp = Integer.toHexString(b[n] & 0xff);
if (stmp.length() == 1) {
hs = hs + "0" + stmp;
} else {
hs = hs + stmp;
}
}
return hs.toUpperCase();
}
5:读取Rss
mClient.readRssi(MAC, new BleReadRssiResponse() {
@Override
public void onResponse(int code, Integer rssi) {
if(code == REQUEST_SUCCESS) {
}
}
});
6.关闭Notify
mClient.unnotify(MAC, serviceUUID, characterUUID, new BleUnnotifyResponse() {
@Override
public void onResponse(int code) {
if(code == REQUEST_SUCCESS) {
}
}
});
7.关闭Indicate
mClient.unindicate(MAC, serviceUUID, characterUUID, new BleUnnotifyResponse() {
@Override
public void onResponse(int code) {
if(code == REQUEST_SUCCESS) {
}
}
});
3.总结
蓝牙连接设备时要判断好连接状态和搜索状态等与页面的关系,避免出现空指针的情况。有什么要讨论的问题可以留言呦。