最近做一款Android与蓝牙BLE设备通讯的项目,记录下开发经验。
蓝牙设备是JDY-19模块,串口透传,非常方便好用。官方教程需要创建Service进行通讯,此处需求为简单数据透传,直接在Activity中收发完成就结束,不开启服务,简单便捷。话不多说,代码伺候。
一、Android扫描BLE设备
0. 开启权限
1 2 3 4 5 |
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" /> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> |
1. 检查是否有BLE支持
1 2 3 4 5 |
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { Toast.makeText(this, "您的设备不支持蓝牙BLE", Toast.LENGTH_SHORT).show(); finish(); } |
2.检查是否有蓝牙支持
1 2 3 4 5 6 7 |
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter(); if (mBluetoothAdapter == null) { Toast.makeText(this, "您的设备不支持蓝牙", Toast.LENGTH_SHORT).show(); finish(); } |
3.如果本地蓝牙没有开启,请求打开
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
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.扫描结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
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通讯
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
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指针
1 2 |
bleControl = new BLEControl(this, mDevice, this); |
1.实现事件监听器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
@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.连接设备
1 2 |
bleControl.connect(); |
3.断开设备
1 |
bleControl.disconnect(); |
原创文章,转载请注明: 转载自贝壳博客
本文链接地址: Android BLE通讯详解,连接JDY-19