最近做一款Android与蓝牙BLE设备通讯的项目,记录下开发经验。
蓝牙设备是JDY-19模块,串口透传,非常方便好用。官方教程需要创建Service进行通讯,此处需求为简单数据透传,直接在Activity中收发完成就结束,不开启服务,简单便捷。话不多说,代码伺候。
一、Android扫描BLE设备
0. 开启权限
1. 检查是否有BLE支持
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, "您的设备不支持蓝牙BLE", Toast.LENGTH_SHORT).show();
finish();
}
2.检查是否有蓝牙支持
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
Toast.makeText(this, "您的设备不支持蓝牙", Toast.LENGTH_SHORT).show();
finish();
}
3.如果本地蓝牙没有开启,请求打开
if (!mBluetoothAdapter.isEnabled()) {
//两种方式
//1.请求用户打开
// 我们通过startActivityForResult()方法发起的Intent将会在onActivityResult()回调方法中获取用户的选择,比如用户单击了Yes开启,
// 那么将会收到RESULT_OK的结果,
// 如果RESULT_CANCELED则代表用户不愿意开启蓝牙
Intent mIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(mIntent, REQUEST_ENABLE_BT);
// 2.用enable()方法来开启,无需询问用户(无声息的开启蓝牙设备),这时就需要用到android.permission.BLUETOOTH_ADMIN权限。
//mBluetoothAdapter.enable();
// mBluetoothAdapter.disable();//关闭蓝牙
}
4.开始扫描LE设备,这个API要求 targetSdkVersion 必须低于21 , minSdkVersion必须大于等于18
scanLeDevice(true);
private void scanLeDevice(final boolean enable) {
if (enable) {
// Stops scanning after a pre-defined scan period.
//是否需要自动停止扫描
// mHandler.postDelayed(new Runnable() {
// @Override
// public void run() {
// mScanning = false;
// mBluetoothAdapter.stopLeScan(mLeScanCallback);
// }
// }, SCAN_PERIOD); //
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}
5.扫描结果
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
runOnUiThread(new Runnable() {
@Override
public void run() {
for (Map<String, Object> item : mData) {
if (item.get("mac").equals(device.getAddress())) {
item.put("rssi",rssi);
adapter.notifyDataSetChanged();
return;
}
}
Log.i("CTLockBLE", "device found name:" + device.getName() + " mac:" + device.getAddress() + " type:" + device.getType() + " rssi:" + rssi);
if (device.getAddress() == null) {
return;
}
if (scanRecord.length != 62 || scanRecord[20 - 6] != (byte) 0xa0) {
//不是透传模块
return;
}
Map<String, Object> itemData = new HashMap<>();
itemData.put("dev",device);
itemData.put("name", device.getName());
itemData.put("mac", device.getAddress());
itemData.put("type", device.getType());
itemData.put("rssi", rssi);
itemData.put("sr", scanRecord);
mData.add(itemData);
adapter.notifyDataSetChanged();
}
});
}
};
6.保存扫描结果
一次连接需要的数据是扫描后的device对象,连接哪个设备就使用这个device对象进行后续连接
二、与蓝牙BLE通讯
package cn.nodemedia.ctlockble;
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.BluetoothProfile;
import android.content.Context;
import android.util.Log;
public class BLEControl {
public interface BLEControlCallback {
void onConnStatus(int status);
}
private final static String TAG = "CTBLE.BLEControl";
private static final int STATE_DISCONNECTED = 0;
private static final int STATE_CONNECTING = 1;
private static final int STATE_CONNECTED = 2;
public static String Service_uuid = "0000ffe0-0000-1000-8000-00805f9b34fb";
public static String Characteristic_uuid_TX = "0000ffe1-0000-1000-8000-00805f9b34fb";
public static String Characteristic_uuid_RX = "00002902-0000-1000-8000-00805f9b34fb";
public static String Characteristic_uuid_FUNCTION = "0000ffe2-0000-1000-8000-00805f9b34fb";
private Context mContext;
private BluetoothDevice mDevice;
private BluetoothGatt mBluetoothGatt;
private BLEControlCallback mCallback;
private int mConnectionState = STATE_DISCONNECTED;
private boolean isSubscribe = false;
public BLEControl(Context context, BluetoothDevice device, BLEControlCallback callback) {
this.mContext = context;
this.mDevice = device;
this.mCallback = callback;
}
public int connect() {
mConnectionState = STATE_CONNECTING;
this.mBluetoothGatt = mDevice.connectGatt(mContext, true, mGattCallback);
return this.mBluetoothGatt == null ? -1 : 0;
}
public void disconnect() {
if (mBluetoothGatt != null) {
mCallback.onConnStatus(1004);
mBluetoothGatt.close();
}
}
void deley(int ms) {
try {
Thread.currentThread();
Thread.sleep(ms);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public int updateRom() {
return 0;
}
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
mConnectionState = STATE_CONNECTED;
Log.i(TAG, "Connected to GATT server.");
Log.i(TAG, "Attempting to start service discovery:" + mBluetoothGatt.discoverServices());
// runOnUiThread(() -> );
mCallback.onConnStatus(1000);
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
mConnectionState = STATE_DISCONNECTED;
Log.i(TAG, "Disconnected from GATT server.");
// runOnUiThread(() -> stateTv.setText("设备已断开"));
mCallback.onConnStatus(1002);
}
}
@Override
// New services discovered
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(TAG, "onServicesDiscovered received: " + status);
for (BluetoothGattService service : mBluetoothGatt.getServices()) {
Log.d(TAG, "get service uuid " + service.getUuid() + " type:" + service.getType());
for (BluetoothGattCharacteristic gc : service.getCharacteristics()) {
Log.d(TAG, "get service gc uuid " + gc.getUuid());
for (BluetoothGattDescriptor descriptor : gc.getDescriptors()) {
Log.d(TAG, "get service gc descriptor uuid " + descriptor.getUuid());
isSubscribe = mBluetoothGatt.setCharacteristicNotification(gc, true);
if (isSubscribe) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
mCallback.onConnStatus(1001);
} else {
Log.e(TAG, "setCharacteristicNotification error");
mCallback.onConnStatus(1003);
}
}
}
}
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
@Override
// Result of a characteristic read operation
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(TAG, "onCharacteristicRead received: " + status);
}
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
Log.d(TAG, "onCharacteristicChanged " + characteristic.getValue().length);
}
};
}
0.创建对象
传入Context对象,扫描后得到的device对象,当前Activity类implements BLEControl.BLEControlCallback后的 this指针
bleControl = new BLEControl(this, mDevice, this);
1.实现事件监听器
@Override
public void onConnStatus(int status) {
Log.d("CTBLE.CommActivity", "OnConnStatus " + status);
runOnUiThread(() -> {
switch (status) {
case 1000:
stateTv.setText("设备已连接");
break;
case 1001:
stateTv.setText("设备已订阅");
break;
case 1002:
stateTv.setText("设备已断开");
break;
case 1003:
stateTv.setText("设备订阅失败");
break;
case 1004:
stateTv.setText("设备已注销");
break;
}
});
}
2.连接设备
bleControl.connect();
3.断开设备
bleControl.disconnect();
原创文章,转载请注明: 转载自贝壳博客
本文链接地址: Android BLE通讯详解,连接JDY-19