android Ble通信
来源:互联网 发布:js 获取tr 再获取td 编辑:程序博客网 时间:2024/05/21 12:36
一.BLE介绍
BLE是Bluetooth Low Energy的缩写,又叫蓝牙4.0,区别于蓝牙3.0和之前的技术。BLE前身是NOKIA开发的Wibree技术,主要用于实现移动智能终端与周边配件之间的持续连接,是功耗极低的短距离无线通信技术,并且有效传输距离被提升到了100米以上,同时只需要一颗纽扣电池就可以工作数年之久。BLE是在蓝牙技术的基础上发展起来的,既同于蓝牙,又区别于传统蓝牙。BLE设备分单模和双模两种,双模简称BR,商标为Bluetooth Smart Ready,单模简称BLE或者LE,商标为Bluetooth Smart。Android是在4.3后才支持BLE,这说明不是所有蓝牙手机都支持BLE,而且支持BLE的蓝牙手机一般是双模的。双模兼容传统蓝牙,可以和传统蓝牙通信,也可以和BLE通信,常用在手机上,android4.3和IOS4.0之后版本都支持BR,也就是双模设备。单模只能和BR和单模的设备通信,不能和传统蓝牙通信,由于功耗低,待机长,所以常用在手环的智能设备上。
二、基本概念
Generic Access Profile(GAP)
用来控制设备连接和广播,GAP使你的设备被其他设备可见,并决定了你的设备是否可以或者怎样与合同设备进行交互。Generic Attribute Profile(GATT)
通过BLE连接,读写属性类数据的Profile通用规范,现在所有的BLE应用Profile都是基于GATT的。Attribute Protocol (ATT)
GATT是基于ATTProtocol的,ATT针对BLE设备做了专门的优化,具体就是在传输过程中使用尽量少的数据,每个属性都有一个唯一的UUID,属性将以characteristics and services的形式传输。Characteristic
Characteristic可以理解为一个数据类型,它包括一个value和0至多个对次value的描述(Descriptor)。Descriptor
对Characteristic的描述,例如范围、计量单位等。Service
Characteristic的集合。例如一个service叫做“Heart Rate Monitor”,它可能包含多个Characteristics,其中可能包含一个叫做“heart ratemeasurement”的Characteristic。UUID
唯一标示符,每个Service,Characteristic,Descriptor,都是由一个UUID定义。
三、Android BLE API
BluetoothManager:
通过BluetoothManager来获取BluetoothAdapter。
BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);BluetoothAdapter:
代表了移动设备的本地的蓝牙适配器, 通过该蓝牙适配器可以对蓝牙进行基本操作,一个Android系统只有一个BluetoothAdapter,通过BluetoothManager获取。
BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();BluetoothDevice:
扫描后发现可连接的设备,获取已经连接的设备。
BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(address);BluetoothGatt:
继承BluetoothProfile,通过BluetoothGatt可以连接设备(connect),发现服务(discoverServices),并把相应地属性返回到BluetoothGattCallback,可以看成蓝牙设备从连接到断开的生命周期。BluetoothGattCharacteristic:
相当于一个数据类型,可以看成一个特征或能力,它包括一个value和0~n个value的描述(BluetoothGattDescriptor)。BluetoothGattDescriptor:
描述符,对Characteristic的描述,包括范围、计量单位等。BluetoothGattService:
服务,Characteristic的集合。BluetoothProfile:
一个通用的规范,按照这个规范来收发数据。BluetoothGattCallback:
已经连接上设备,对设备的某些操作后返回的结果。- onConnectionStateChange:Callback indicating when GATT client has connected/disconnected to/from a remote GATT server.
- onServicesDiscovered:Callback invoked when the list of remote services, characteristics and descriptors for the remote device have been updated, ie new services have been discovered.
- onCharacteristicChanged:Callback triggered as a result of a remote characteristic notification.
- onCharacteristicRead:Callback reporting the result of a characteristic read operation.
- onCharacteristicWrite:Callback indicating the result of a characteristic write operation.
四、操作流程
google GitHub例子连接BluetoothLe
google Developers Bluetooth SDK reference
首先权限不能忘:
uses-permission android:name=”android.permission.ACCESS_COARSE_LOCATION” />
uses-permission android:name=”android.permission.BLUETOOTH” />
uses-permission android:name=”android.permission.BLUETOOTH_ADMIN” />
除了蓝牙权限外,如果需要BLE feature则还需要声明uses-feature:
uses-featureandroid:name=”android.hardware.bluetooth_le”android:required=”true”/>
按时required为true时,则应用只能在支持BLE的Android设备上安装运行;required为false时,Android设备均可正常安装运行,需要在代码运行时判断设备是否支持BLE feature
1.开启蓝牙
在使用蓝牙BLE之前,需要确认Android设备是否支持BLE feature(required为false时),另外要需要确认蓝牙是否打开。如果发现不支持BLE,则不能使用BLE相关的功能;如果支持BLE,但是蓝牙没打开,则需要打开蓝牙。代码示例如下:
// 检查当前手机是否支持ble 蓝牙,如果不支持退出程序 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show(); finish(); } //初始化 Bluetooth adapter, 通过蓝牙管理器得到一个参考蓝牙适配器(API必须在以上android4.3或以上和版本) final BluetoothManager bluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE); // 检查设备上是否支持蓝牙 mBluetoothAdapter = bluetoothManager.getAdapter(); if (mBluetoothAdapter == null) { Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show(); finish(); return; } //自Android 6.0开始需要打开位置权限才可以搜索到Ble设备 //如果 API level 是大于等于 23(Android 6.0) 时判断是否具有权限, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){ //判断是否具有权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED){ //请求权限 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},REQUEST_CODE_ACCESS_COARSE_LOCATION); } } Boolean result =isLocationEnable(this); if (result){ Log.e(TAG, "用户以打开定位功能==="); }else { setLocationService(); }
执行完上面的请求权限后,系统会弹出提示框让用户选择是否允许改权限。选择的结果可以在回到接口中得知:
//执行完上面的请求权限后,系统会弹出提示框让用户选择是否允许改权限。选择的结果可以在回到接口中得知: @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == REQUEST_CODE_ACCESS_COARSE_LOCATION) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { //用户允许改权限,0表示允许,-1表示拒绝 PERMISSION_GRANTED = 0, PERMISSION_DENIED = -1 //permission was granted, yay! Do the contacts-related task you need to do. //这里进行授权被允许的处理 Log.e(TAG, "允许定位权限==="); } else { //permission denied, boo! Disable the functionality that depends on this permission. //这里进行权限被拒绝的处理 Log.e(TAG, "不允许定位权限==="); } } else { super.onRequestPermissionsResult(requestCode, permissions, grantResults); } }
判断定位:
public final boolean isLocationEnable(Context context) { LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); boolean networkProvider = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER); boolean gpsProvider = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); if (networkProvider || gpsProvider) { return true; } return false; } private void setLocationService() { Intent locationIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); startActivityForResult(locationIntent, REQUEST_CODE_LOCATION_SETTINGS); }
执行完上面的请求权限后回调:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { // 用户没有开启蓝牙 if (requestCode == REQUEST_CODE_LOCATION_SETTINGS) { if (isLocationEnable(this)) { //定位已打开的处理 Toast.makeText(this, "定位已经打开", Toast.LENGTH_SHORT).show(); } else { //定位依然没有打开的处理 Toast.makeText(this, "定位没有打开", Toast.LENGTH_SHORT).show(); } } else if (requestCode == REQUEST_ENABLE_BT && resultCode == RESULT_CANCELED) { finish(); return; } super.onActivityResult(requestCode, resultCode, data); }
2、设备搜索
BluetoothAdapter.startDiscovery在大多数手机上是可以同时发现经典蓝牙和Ble的,但是startDiscovery的回调无法返回Ble的广播,所以无法通过广播识别设备,且startDiscovery扫描Ble的效率比StartLeScan低很多。所以在实际应用中,还是StartDiscovery和StartLeScan分开扫,前者扫传统蓝牙,后者扫低功耗蓝牙。
由于搜索需要尽量减少功耗,因此在实际使用时需要注意:当找到对应的设备后,立即停止扫描;不要循环搜索设备,为每次搜索设置适合的时间限制,避免设备不在可用范围的时候持续不停扫描,消耗电量。
通过调用BluetoothAdapter的 startLeScan() 搜索BLE设备。调用此方法时需要传入 BluetoothAdapter.LeScanCallback 参数。具体代码示例如下:
/** * Device scan callback. * BluetoothAdapter.LeScanCallback :public static interface * onLeScan:Callback reporting an LE device found during a device scan initiated by the startLeScan(BluetoothAdapter.LeScanCallback) function. */private final 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(); } }); } };
3、设备通信
两个设备通过BLE通信,首先需要建立GATT连接,这里我们讲的是Android设备作为client端,连接GATT Server。连接GATT Server需要调用BluetoothDevice的connectGatt()方法,此函数带三个参数:Context、autoConnect(boolean)和 BluetoothGattCallback 对象。调用后返回BluetoothGatt对象,它是GATT profile的封装,通过这个对象,我们就能进行GATT Client端的相关操作。如断开连接bluetoothGatt.disconnect(),发现服务bluetoothGatt.discoverServices()等等。示例代码如下:
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address); if (device == null) { Log.w(TAG, "Device not found. Unable to connect."); return false; } // We want to directly connect to the device, so we are setting the autoConnect // parameter to false. //Connect to GATT Server hosted by this device. mBluetoothGatt = device.connectGatt(this, false, mGattCallback); Log.d(TAG, "Trying to create a new connection."); /** * BluetoothGattCallback: public abstract class * onConnectionStateChange:Callback indicating when GATT client has connected/disconnected to/from a remote GATT server. * onServicesDiscovered:Callback invoked when the list of remote services, characteristics and descriptors for the remote device have been updated, ie new services have been discovered. * onCharacteristicChanged:Callback triggered as a result of a remote characteristic notification. * onCharacteristicRead:Callback reporting the result of a characteristic read operation. * onCharacteristicWrite:Callback indicating the result of a characteristic write operation. */ private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback(){ @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { super.onConnectionStateChange(gatt, status, newState); Log.i(TAG, "onConnectionStateChange.newState = " + newState); String intentAction; if (newState == BluetoothProfile.STATE_CONNECTED) { intentAction = ACTION_GATT_CONNECTED; mConnectionState = STATE_CONNECTED; broadcastUpdate(intentAction); Log.i(TAG, "Connected to GATT server."); // Attempts to discover services after successful connection. 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 public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); Log.i(TAG, "onServicesDiscovered."); if (status == BluetoothGatt.GATT_SUCCESS) { broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED); } else { Log.w(TAG, "onServicesDiscovered received: " + status); } } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); Log.i(TAG, "onCharacteristicChanged."); //broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicRead(gatt, characteristic, status); Log.i(TAG, "onCharacteristicRead.status = " + status); if (status == BluetoothGatt.GATT_SUCCESS) { broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); } } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); Log.i(TAG, "onCharacteristicWrite.status = " + status); if (status == BluetoothGatt.GATT_SUCCESS) { broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic); } } };
以下为获得Gatt后的相关操作对应的响应方法
//notification to onCharacteristicChanged;
bluetoothGatt.setCharacteristicNotification(characteristic, true);
//readCharacteristic to onCharacteristicRead;
bluetoothGatt.readCharacteristic(characteristic);
//writeCharacteristic to onCharacteristicWrite;
bluetoothGatt.wirteCharacteristic(mCurrentcharacteristic);
//connect and disconnect to onConnectionStateChange;
bluetoothGatt.connect();
bluetoothGatt.disconnect();
//readDescriptor to onDescriptorRead;
bluetoothGatt.readDescriptor(descriptor);
//writeDescriptor to onDescriptorWrite;
bluetoothGatt.writeDescriptor(descriptor);
//readRemoteRssi to onReadRemoteRssi;
bluetoothGatt.readRemoteRssi();
//executeReliableWrite to onReliableWriteCompleted;
bluetoothGatt.executeReliableWrite();
//discoverServices to onServicesDiscovered;
bluetoothGatt.discoverServices();
- Android BLE 扫描 通信
- Android BLE蓝牙通信
- Android 蓝牙Ble通信
- android Ble通信
- Android BLE 通信处理过程---串行通信
- Android BLE开发之Android手机与BLE终端通信
- Android BLE开发之Android手机与BLE终端通信
- Android BLE开发之Android手机与BLE终端通信
- Android BLE开发之Android手机与BLE终端通信
- Android BLE开发之Android手机与BLE终端通信
- Android BLE开发之Android手机与BLE终端通信
- Android BLE教程(和单片机通信)
- Android BLE设备蓝牙通信框架BluetoothKit
- Android手机与BLE终端通信
- Android与蓝牙Ble之间的通信
- Android提高之Android手机与BLE终端通信
- 【转】Android BLE开发之Android手机与BLE终端通信
- Android BLE开发——Android手机与BLE终端通信初识
- maven编译时java.lang.ArrayIndexOutOfBoundsException
- QT中的智能指针
- 干货看再多,也不一定能成为优秀产品经理
- 替换tomcat中webapp里的前端文件,浏览器没有检查到更新
- 单例模式在多线程下的安全性
- android Ble通信
- 关于求最大公约数gcd的一些证明
- Spring boot + Websocket 初篇
- 产品经理,要“看懂”、更要“看破”
- 心理估值:什么样的价值对用户有意义?
- javaweb工程 servlet文件上传与下载
- 触发欢迎回家-如何minecraft检测人物是否在某一位置
- 2017人人都是产品经理年度作家评选|为产品圈和运营圈的创作者打Call!
- sysbench测试