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就是需要发送的数据)
然后就是收数据....
相关文章推荐
- Android读书笔记(一)Activity退出时释放内存
- Android源码桥梁模式---FragmentCompat
- Android数据传输格式(基于JSON格式)
- android中修改tablayout中的字体大小和颜色
- Android中Adapter的notifyDataSetInvalidated()和notifyDataSetChanged()的区别!
- android.support.design.widget.TabLayout的使用
- Android技术知识分享第一弹——SDK中常用命令
- Android开发中通过源码彻底理解ListView工作原理
- Android框架xUtils简介
- Android Service生命周期 Service里面的onStartCommand()方法详解
- Android bind service讲解以及跨进程通信
- Android开发艺术探索——第四章View的工作原理
- Android之RadioGroup+ViewPager制作的底部导航栏
- android开发步步为营之100:开源项目SlidingUpPanelLayout简单用法
- [原创]Android系统中常用JAVA类源码浅析之HashMap
- 如何为Android缓存数据到本地
- 解决Android TextView默认的padding问题
- Android进阶之Property Animator研究
- Android布局文件参数layout_alignTop的作用
- 加快Android编译速度