这篇博客想写很久了,只是之前一直提不起劲,刚好最近还是一如既往的闲得蛋疼,那就写写吧,免得自己都忘了!
刚进公司的时候,做的就是BLE的项目,随着这个项目的不了了之,我也忘了这事。
BLE的全名是 Bluetooth Low Energy 就是低功耗蓝牙的意思,支持 API18(Android 4.3)及以上的设备,本文将说明如何通过BLE实现数据的收发
如果你的app只是用BLE的话,在manifest.xml里加以下权限就好了:
android:name="android.hardware.bluetooth_le" android:required="true"/>
如果想让App在不支持 BLE(即API<18)的设备上也能运行,那么应该将上面权限中的required设成false。同时,加上下面的代码,
// Use this check to determine whether BLE is supported on the device. Then // you can selectively disable BLE-related features. if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show(); finish(); }代码的作用就是当设备不支持BLE时,弹出Toast告诉用户 添加完权限后,我们便可以敲代码了先把BLE给打开,这里涉及到BluetoothAdapter和BluetoothManger对象,一个支持BLE的android设备有且只有一个BluetoothAdapter,并通过BluetoothManger来获取,上代码
// Initializes Bluetooth adapter. BluetoothAdapter mBluetoothAdapter; final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter();然后是打开BLE:
private BluetoothAdapter mBluetoothAdapter; ... // Ensures Bluetooth is available on the device and it is enabled. If not, // displays a dialog requesting user permission to enable Bluetooth. if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);//这时会弹出对话框,询问用户是否打开BLE }搜索周边的蓝牙设备:
/** * Activity for scanning and displaying available BLE devices. */ public class DeviceScanActivity extends ListActivity { private BluetoothAdapter mBluetoothAdapter; private boolean mScanning; private Handler mHandler; // Stops scanning after 10 seconds. private static final long SCAN_PERIOD = 10000; ... 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);//mLeScanCallback是一个搜索设备的回调参数,待会贴它的代码 } }, SCAN_PERIOD); mScanning = true; mBluetoothAdapter.startLeScan(mLeScanCallback);//开始搜索 } else { mScanning = false; mBluetoothAdapter.stopLeScan(mLeScanCallback); } ...// } ...//这里的...是设置ListActivity的Adapter的代码,在mLeScanCallback的代码中会提到 }LeScanCallback是设备搜索周边其他蓝牙设备时,回调的接口对象
private LeDeviceListAdapter mLeDeviceListAdapter; ... // Device scan callback. private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { runOnUiThread(new Runnable() { @Override public void run() { mLeDeviceListAdapter.addDevice(device); mLeDeviceListAdapter.notifyDataSetChanged(); } }); } }; ////////////////////////////////////////////////////////////////////////////////// public class LeDeviceListAdapter extends BaseAdapter{ private Listdevices; public LeDeviceListAdapter(){ devices = new ArrayList<>(); ..... } public void addDevice(BluetoothDevice mdevice){ if(!devices.contains(mdevice)){ devices.add(mdevice); } } ....... @Override public View getView(int position ,View view ,ViewGroup viewGroup){ BluetoothDevice bd = devices.get(position); String deviceName = bd.getName();//获取周边设备的名字,然后就可以在ListActivity上显示了 } .....................//至于viewholder的代码就不写了 }
接下来就到了收发数据了
先来了解一下GATT是什么鬼,GATT是通过蓝牙收发数据的一种协议,包含Characteristic、Descriptor、Service三个属性值
BLE分为三部分Service、Characteristic、Descriptor,这三部分都由UUID作为唯一标示符。一个蓝牙4.0的终端可以
包含多个Service,一个Service可以包含多个Characteristic,一个Characteristic包含一个Value和多个Descriptor
,一个Descriptor包含一个Value
先连GATT Server
mBluetoothGatt = device.connectGatt(Context context, boolean autoConnect, BluetoothGattCallback mGattCallback); BluetoothGattCallback主要是监控蓝牙的连接状态,并发出广播,下面一大段都是关于广播的处理BluetoothGattCallback回调的实现:
// A service that interacts with the BLE device via the Android BLE API. public class BluetoothLeService extends Service { private final static String TAG = BluetoothLeService.class.getSimpleName(); private BluetoothManager mBluetoothManager; private BluetoothAdapter mBluetoothAdapter; private String mBluetoothDeviceAddress; private BluetoothGatt mBluetoothGatt; private int mConnectionState = STATE_DISCONNECTED; private static final int STATE_DISCONNECTED = 0; private static final int STATE_CONNECTING = 1; private static final int STATE_CONNECTED = 2; public final static String ACTION_GATT_CONNECTED = "com.example.bluetooth.le.ACTION_GATT_CONNECTED"; public final static String ACTION_GATT_DISCONNECTED = "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED"; public final static String ACTION_GATT_SERVICES_DISCOVERED = "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED"; public final static String ACTION_DATA_AVAILABLE = "com.example.bluetooth.le.ACTION_DATA_AVAILABLE"; public final static String EXTRA_DATA = "com.example.bluetooth.le.EXTRA_DATA"; public final static UUID UUID_HEART_RATE_MEASUREMENT = UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT); // Various callback methods defined by the BLE API. private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { String intentAction; if (newState == BluetoothProfile.STATE_CONNECTED) { intentAction = ACTION_GATT_CONNECTED; mConnectionState = STATE_CONNECTED; broadcastUpdate(intentAction); Log.i(TAG, "Connected to GATT server."); Log.i(TAG, "Attempting to start service discovery:" + mBluetoothGatt.discoverServices()); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { intentAction = ACTION_GATT_DISCONNECTED; mConnectionState = STATE_DISCONNECTED; Log.i(TAG, "Disconnected from GATT server."); broadcastUpdate(intentAction); } } @Override // New services discovered public void onServicesDiscovered(BluetoothGatt gatt, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED); } 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) { broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); } } ... }; ... }好长的代码,其实就是监听蓝牙的连接状态,并向系统发出广播
broadcastUpdate()的代码:
private void broadcastUpdate(final String action) { final Intent intent = new Intent(action); sendBroadcast(intent); } private void broadcastUpdate(final String action, final BluetoothGattCharacteristic characteristic) { final Intent intent = new Intent(action); // This is special handling for the Heart Rate Measurement profile. Data // parsing is carried out as per profile specifications. if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) { int flag = characteristic.getProperties(); int format = -1; if ((flag & 0x01) != 0) { format = BluetoothGattCharacteristic.FORMAT_UINT16; Log.d(TAG, "Heart rate format UINT16."); } else { format = BluetoothGattCharacteristic.FORMAT_UINT8; Log.d(TAG, "Heart rate format UINT8."); } final int heartRate = characteristic.getIntValue(format, 1); Log.d(TAG, String.format("Received heart rate: %d", heartRate)); intent.putExtra(EXTRA_DATA, String.valueOf(heartRate)); } else { // For all other profiles, writes the data formatted in HEX. final byte[] data = characteristic.getValue(); if (data != null && data.length > 0) { final StringBuilder stringBuilder = new StringBuilder(data.length); for(byte byteChar : data) stringBuilder.append(String.format("%02X ", byteChar)); intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString()); } } sendBroadcast(intent); }有广播发出,当然就要对广播进行处理了
// Handles various events fired by the Service. // ACTION_GATT_CONNECTED: connected to a GATT server. // ACTION_GATT_DISCONNECTED: disconnected from a GATT server. // ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services. // ACTION_DATA_AVAILABLE: received data from the device. This can be a // result of read or notification operations. private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) { mConnected = true; updateConnectionState(R.string.connected); invalidateOptionsMenu();//对菜单按钮的状态进行更新 } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) { mConnected = false; updateConnectionState(R.string.disconnected); invalidateOptionsMenu(); clearUI(); } else if (BluetoothLeService. ACTION_GATT_SERVICES_DISCOVERED.equals(action)) { // Show all the supported services and characteristics on the // user interface. displayGattServices(mBluetoothLeService.getSupportedGattServices()); } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) { displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA)); } } };
扯了一大堆,终于可以发数据了!!!官网没有介绍,只能手写了.......
public boolean sendCharacteristic(){
String kUUIDWriteCommand = "0000FEC7-0000-1000-8000-00805F9B34FB";//"0000FF01-0000-1000-8000-00805F9B34FB";
UUID writeUUID = UUID.fromString(kUUIDWriteCommand);
BluetoothGattCharacteristic btWriteGattChar = gattService.getCharacteristic(writeUUID);
boolean right = divideFrameBleSendData(data, btWriteGattChar);
}
private static boolean divideFrameBleSendData(byte[] data, BluetoothGattCharacteristic btWriteGattChar){这样,就把数据给发过去了(参数中的data就是需要发送的数据)
然后就是收数据....