您的位置:首页 > 移动开发 > Android开发

Android BLE浅析

2016-04-26 19:49 417 查看
这篇博客想写很久了,只是之前一直提不起劲,刚好最近还是一如既往的闲得蛋疼,那就写写吧,免得自己都忘了!   
刚进公司的时候,做的就是BLE的项目,随着这个项目的不了了之,我也忘了这事。   
BLE的全名是 Bluetooth Low Energy 就是低功耗蓝牙的意思,支持 API18(Android 4.3)及以上的设备,本文将说明如何通过BLE实现数据的收发
如果你的app只是用BLE的话,在manifest.xml里加以下权限就好了:
<uses-feature 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 List<BluetoothDevice> devices;

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() {@Overridepublic 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 discoveredpublic 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 operationpublic 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() {@Overridepublic 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));}}};
扯了一大堆,终于可以发数据了!!!官网没有介绍,只能手写了.......
<span style="font-family:Arial;">public boolean sendCharacteristic(){</span>
<span style="font-family:Arial;">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);</span>
<span style="font-family:Arial;"></span>
<span style="font-family:Arial;">}</span>
发送给蓝牙的数据要分包发,太长的数据一次性是发布过去的,
<span style="font-family:Arial;"></span>
private static boolean divideFrameBleSendData(byte[] data, BluetoothGattCharacteristic btWriteGattChar){boolean right = false;int tmpLen = data.length;int start = 0;int end = 0;while (tmpLen > 0){byte[] sendData = new byte[21];if (tmpLen >= 20){end += 20;sendData = Arrays.copyOfRange(data, start, end);start += 20;tmpLen -= 20;}else{end += tmpLen;sendData = Arrays.copyOfRange(data, start, end);tmpLen = 0;}right = btWriteGattChar.setValue(sendData);//发数据if (!right){MyUtil.writeLog("btWriteGattChar.setValue false! data=" + MyUtil.binToHex(data, 0, data.length));return false;}right = mBluetoothGatt.writeCharacteristic(btWriteGattChar);if (!right) return right;}return right;}
这样,就把数据给发过去了(参数中的data就是需要发送的数据)
然后就是收数据....

                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: