本文来自http://blog.csdn.net/hellogv/ ,引用必须注明出处!

近期穿戴设备发展得非常火。把相关技术也带旺了,当中一项是BLE(Bluetooth Low Energy)。BLE是蓝牙4.0的核心Profile,主打功能是高速搜索,高速连接。超低功耗保持连接和数据传输,弱点是数据传输速率低,因为BLE的低功耗特点,因此普遍用于穿戴设备。Android 4.3才開始支持BLE API,所以请各位客官把本文代码执行在蓝牙4.0和Android 4.3及其以上的系统,另外本文所用的BLE终端是一个蓝牙4.0的串口蓝牙模块。

PS:我的i9100刷了4.4系统后,居然也能跟BLE蓝牙模块通信。

BLE分为三部分Service、Characteristic、Descriptor,这三部分都由UUID作为唯一标示符。

一个蓝牙4.0的终端能够包括多个Service,一个Service能够包括多个Characteristic。一个Characteristic包括一个Value和多个Descriptor,一个Descriptor包括一个Value。一般来说,Characteristic是手机与BLE终端交换数据的关键,Characteristic有较多的跟权限相关的字段,比如PERMISSION和PROPERTY,而当中最经常使用的是PROPERTY。本文所用的BLE蓝牙模块居然没有标准的Characteristic的PERMISSION。Characteristic的PROPERTY能够通过位运算符组合来设置读写属性,比如READ|WRITE、READ|WRITE_NO_RESPONSE|NOTIFY,因此读取PROPERTY后要分解成所用的组合(本文代码已含此分解方法)。

本文代码改自Android 4.3 Sample的BluetoothLeGatt。把冗余代码去掉,获取的BLE设备信息都通过Log。另一些必要的读写蓝牙方法,应该算是简化到大家一看就能够懂了。

本文代码能够到http://download.csdn.net/detail/hellogv/7228819下载。接下来贴出本文执行的结果,首先是连接BLE设备后。枚举出设备全部Service、Characteristic、Descriptor,而且手机会往Characteristic uuid=0000ffe1-0000-1000-8000-00805f9b34fb写入“send data->”字符串,BLE终端收到数据通过串口传到PC串口助手(见PC串口助手的截图):

04-21 18:28:25.465: E/DeviceScanActivity(12254): -->service type:PRIMARY
04-21 18:28:25.465: E/DeviceScanActivity(12254): -->includedServices size:0
04-21 18:28:25.465: E/DeviceScanActivity(12254): -->service uuid:00001800-0000-1000-8000-00805f9b34fb
04-21 18:28:25.465: E/DeviceScanActivity(12254): ---->char uuid:00002a00-0000-1000-8000-00805f9b34fb
04-21 18:28:25.465: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.465: E/DeviceScanActivity(12254): ---->char property:READ
04-21 18:28:25.465: E/DeviceScanActivity(12254): ---->char uuid:00002a01-0000-1000-8000-00805f9b34fb
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char property:READ
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char uuid:00002a02-0000-1000-8000-00805f9b34fb
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char property:READ|WRITE|
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char uuid:00002a03-0000-1000-8000-00805f9b34fb
04-21 18:28:25.470: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.475: E/DeviceScanActivity(12254): ---->char property:READ|WRITE|
04-21 18:28:25.475: E/DeviceScanActivity(12254): ---->char uuid:00002a04-0000-1000-8000-00805f9b34fb
04-21 18:28:25.475: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.475: E/DeviceScanActivity(12254): ---->char property:READ
04-21 18:28:25.475: E/DeviceScanActivity(12254): -->service type:PRIMARY
04-21 18:28:25.475: E/DeviceScanActivity(12254): -->includedServices size:0
04-21 18:28:25.475: E/DeviceScanActivity(12254): -->service uuid:00001801-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char uuid:00002a05-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char property:INDICATE
04-21 18:28:25.480: E/DeviceScanActivity(12254): -------->desc uuid:00002902-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: E/DeviceScanActivity(12254): -------->desc permission:UNKNOW
04-21 18:28:25.480: E/DeviceScanActivity(12254): -->service type:PRIMARY
04-21 18:28:25.480: E/DeviceScanActivity(12254): -->includedServices size:0
04-21 18:28:25.480: E/DeviceScanActivity(12254): -->service uuid:0000ffe0-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char uuid:0000ffe1-0000-1000-8000-00805f9b34fb
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char permission:UNKNOW
04-21 18:28:25.480: E/DeviceScanActivity(12254): ---->char property:READ|WRITE_NO_RESPONSE|NOTIFY|
04-21 18:28:25.490: E/DeviceScanActivity(12254): -------->desc uuid:00002902-0000-1000-8000-00805f9b34fb
04-21 18:28:25.490: E/DeviceScanActivity(12254): -------->desc permission:UNKNOW
04-21 18:28:25.490: E/DeviceScanActivity(12254): -------->desc uuid:00002901-0000-1000-8000-00805f9b34fb
04-21 18:28:25.490: E/DeviceScanActivity(12254): -------->desc permission:UNKNOW
04-21 18:28:26.025: E/DeviceScanActivity(12254): onCharRead BLE DEVICE read 0000ffe1-0000-1000-8000-00805f9b34fb -> 00

这里红字是由BluetoothGattCallback的onCharacteristicRead()回调而打出Log

下面Log是PC上的串口工具通过BLE模块发送过来。由BluetoothGattCallback的 onCharacteristicChanged()打出Log
04-21 18:30:18.260: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:18.745: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:19.085: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:19.350: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:19.605: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:19.835: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:20.055: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:20.320: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:20.510: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:20.735: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone
04-21 18:30:21.000: E/DeviceScanActivity(12254): onCharWrite BLE DEVICE write 0000ffe1-0000-1000-8000-00805f9b34fb -> send data to phone

接下来贴出本文核心代码:

public class DeviceScanActivity extends ListActivity {
private final static String TAG = DeviceScanActivity.class.getSimpleName();
private final static String UUID_KEY_DATA = "0000ffe1-0000-1000-8000-00805f9b34fb"; private LeDeviceListAdapter mLeDeviceListAdapter;
/**搜索BLE终端*/
private BluetoothAdapter mBluetoothAdapter;
/**读写BLE终端*/
private BluetoothLeClass mBLE;
private boolean mScanning;
private Handler mHandler; // Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 10000; @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActionBar().setTitle(R.string.title_devices);
mHandler = new Handler(); // 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();
} // Initializes a Bluetooth adapter. For API level 18 and above, get a reference to
// BluetoothAdapter through BluetoothManager.
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter(); // Checks if Bluetooth is supported on the device.
if (mBluetoothAdapter == null) {
Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
finish();
return;
}
//开启蓝牙
mBluetoothAdapter.enable(); mBLE = new BluetoothLeClass(this);
if (!mBLE.initialize()) {
Log.e(TAG, "Unable to initialize Bluetooth");
finish();
}
//发现BLE终端的Service时回调
mBLE.setOnServiceDiscoverListener(mOnServiceDiscover);
//收到BLE终端数据交互的事件
mBLE.setOnDataAvailableListener(mOnDataAvailable);
} @Override
protected void onResume() {
super.onResume(); // Initializes list view adapter.
mLeDeviceListAdapter = new LeDeviceListAdapter(this);
setListAdapter(mLeDeviceListAdapter);
scanLeDevice(true);
} @Override
protected void onPause() {
super.onPause();
scanLeDevice(false);
mLeDeviceListAdapter.clear();
mBLE.disconnect();
} @Override
protected void onStop() {
super.onStop();
mBLE.close();
} @Override
protected void onListItemClick(ListView l, View v, int position, long id) {
final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);
if (device == null) return;
if (mScanning) {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
mScanning = false;
} mBLE.connect(device.getAddress());
} 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);
invalidateOptionsMenu();
}
}, SCAN_PERIOD); mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
invalidateOptionsMenu();
} /**
* 搜索到BLE终端服务的事件
*/
private BluetoothLeClass.OnServiceDiscoverListener mOnServiceDiscover = new OnServiceDiscoverListener(){ @Override
public void onServiceDiscover(BluetoothGatt gatt) {
displayGattServices(mBLE.getSupportedGattServices());
} }; /**
* 收到BLE终端数据交互的事件
*/
private BluetoothLeClass.OnDataAvailableListener mOnDataAvailable = new OnDataAvailableListener(){ /**
* BLE终端数据被读的事件
*/
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS)
Log.e(TAG,"onCharRead "+gatt.getDevice().getName()
+" read "
+characteristic.getUuid().toString()
+" -> "
+Utils.bytesToHexString(characteristic.getValue()));
} /**
* 收到BLE终端写入数据回调
*/
@Override
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
Log.e(TAG,"onCharWrite "+gatt.getDevice().getName()
+" write "
+characteristic.getUuid().toString()
+" -> "
+new String(characteristic.getValue()));
}
}; // 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();
}
});
}
}; private void displayGattServices(List<BluetoothGattService> gattServices) {
if (gattServices == null) return; for (BluetoothGattService gattService : gattServices) {
//-----Service的字段信息-----//
int type = gattService.getType();
Log.e(TAG,"-->service type:"+Utils.getServiceType(type));
Log.e(TAG,"-->includedServices size:"+gattService.getIncludedServices().size());
Log.e(TAG,"-->service uuid:"+gattService.getUuid()); //-----Characteristics的字段信息-----//
List<BluetoothGattCharacteristic> gattCharacteristics =gattService.getCharacteristics();
for (final BluetoothGattCharacteristic gattCharacteristic: gattCharacteristics) {
Log.e(TAG,"---->char uuid:"+gattCharacteristic.getUuid()); int permission = gattCharacteristic.getPermissions();
Log.e(TAG,"---->char permission:"+Utils.getCharPermission(permission)); int property = gattCharacteristic.getProperties();
Log.e(TAG,"---->char property:"+Utils.getCharPropertie(property)); byte[] data = gattCharacteristic.getValue();
if (data != null && data.length > 0) {
Log.e(TAG,"---->char value:"+new String(data));
} //UUID_KEY_DATA是能够跟蓝牙模块串口通信的Characteristic
if(gattCharacteristic.getUuid().toString().equals(UUID_KEY_DATA)){
//測试读取当前Characteristic数据。会触发mOnDataAvailable.onCharacteristicRead()
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mBLE.readCharacteristic(gattCharacteristic);
}
}, 500); //接受Characteristic被写的通知,收到蓝牙模块的数据后会触发mOnDataAvailable.onCharacteristicWrite()
mBLE.setCharacteristicNotification(gattCharacteristic, true);
//设置数据内容
gattCharacteristic.setValue("send data->");
//往蓝牙模块写入数据
mBLE.writeCharacteristic(gattCharacteristic);
} //-----Descriptors的字段信息-----//
List<BluetoothGattDescriptor> gattDescriptors = gattCharacteristic.getDescriptors();
for (BluetoothGattDescriptor gattDescriptor : gattDescriptors) {
Log.e(TAG, "-------->desc uuid:" + gattDescriptor.getUuid());
int descPermission = gattDescriptor.getPermissions();
Log.e(TAG,"-------->desc permission:"+ Utils.getDescPermission(descPermission)); byte[] desData = gattDescriptor.getValue();
if (desData != null && desData.length > 0) {
Log.e(TAG, "-------->desc value:"+ new String(desData));
}
}
}
}// }
}

Android BLE开发之Android手机与BLE终端通信的更多相关文章

  1. Android BLE开发之Android手机搜索iBeacon基站

    本文来自http://blog.csdn.net/hellogv/ ,引用必须注明出处! 上次讲了Android手机与BLE终端之间的通信,而最常见的BLE终端应该是苹果公司倡导的iBeacon基站. ...

  2. Android NDK开发之Android.mk文件

    Android NDK开发指南---Android.mk文件 博客分类: Android NDK开发指南   Android.mk文件语法详述 介绍: ------------ 这篇文档是用来描述你的 ...

  3. Android安全开发之WebView中的地雷

    Android安全开发之WebView中的地雷 0X01 About WebView 在Android开发中,经常会使用WebView来实现WEB页面的展示,在Activiry中启动自己的浏览器,或者 ...

  4. android软件开发之webView.addJavascriptInterface循环渐进【一】

    本篇文章由:http://www.sollyu.com/android-software-development-webview-addjavascriptinterface-cycle-of-gra ...

  5. Android 异步开发之 AsyncQueryHandler 批量添加联系人

    AsyncQueryHandler: 官方解释是一个异步帮助类(A helper class to help make handling asynchronous ContentResolver qu ...

  6. Android底层开发之Linux输入子系统要不要推断系统休眠状态上报键值

    Android底层开发之Linux输入子系统要不要推断系统休眠状态上报键值 题外话:一个问题研究到最后,那边记录文档的前半部分基本上都是没用的,甚至是错误的. 重点在最后,前边不过一些假想猜測. ht ...

  7. Android混合开发之WebViewJavascriptBridge实现JS与java安全交互

    前言: 为了加快开发效率,目前公司一些功能使用H5开发,这里难免会用到Js与Java函数互相调用的问题,这个Android是提供了原生支持的,不过存在安全隐患,今天我们来学习一种安全方式来满足Js与j ...

  8. Android混合开发之WebView与Javascript交互

    前言: 最近公司的App为了加快开发效率选择了一部分功能采用H5开发,从目前市面的大部分App来讲,大致分成Native App.Web App.Hybrid App三种方式,个人觉得目前以Hybri ...

  9. Android混合开发之WebView使用总结

    前言: 今天修改项目中一个有关WebView使用的bug,激起了我总结WebView的动机,今天抽空做个总结. 混合开发相关博客: Android混合开发之WebView使用总结 Android混合开 ...

随机推荐

  1. [Redux] React Todo List Example (Toggling a Todo)

    /** * A reducer for a single todo * @param state * @param action * @returns {*} */ const todo = ( st ...

  2. css伪类选择器详细解析及案例使用-----伪类选择器(1)

    动态伪类选择器:E:link :选择匹配的E元素,并且匹配元素被定义了超链接并未被访问过.E:visited :选择匹配的E元素,而且匹配的元素被定义了连接并已被访问过.E:active :选择匹配的 ...

  3. datagrid的基本操作-增删改

    1 ---恢复内容开始--- <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> ...

  4. 基本 XAML 语法指南

    我们介绍了 XAML 语法规则,以及用于描述 XAML 语法中存在的限制或选项的术语.当出现以下情况时你会发现本主题很有用:不熟悉 XAML 语言的使用,希望加强对术语或某些语法部分的理解,或者对 X ...

  5. excel运行最多行数

    2003及以前版本为65536(即6万多)行,256列2007版:1048576(即1百零多万)行,16384(即1千多)列

  6. AJAX 跨域

    1.说到ajax就会遇到的两个问题 1.1AJAX以何种格式来交换数据                    1.自定义字符串 2.XML描述 3.JSON描述(建议使用)          1.2如 ...

  7. Java学习笔记--通过java.net.URLConnection发送HTTP请求

    http://www.cnblogs.com/nick-huang/p/3859353.html 使用Java API发送 get请求或post请求的步骤: 1. 通过统一资源定位器(java.net ...

  8. Leakcanary

    一.什么是内存泄漏 垃圾回收器无法回收应被回收的对象比如:在Activity生命周期过程中,旋转屏幕时应新建activity,原activity应被销毁.但如果线程一直在引用此activity,则会出 ...

  9. iOS开发 masonry 设置tableHeadView

    最近做公司项目,使用到到tableHeadView,一直习惯用masonry来设置约束,但是设置tableHeadView没有那么的简单.先看下效果图: 视图层次结构是这样的: 基础的创建工程项目之类 ...

  10. Caffe : Layer Catalogue(2)

    TanH / Hyperbolic Tangent 类型(type):TanH CPU 实现: ./src/caffe/layers/tanh_layer.cpp CUDA.GPU实现: ./src/ ...