Android提高之Android手机与BLE终端通信

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

注:笔者的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,还有一些必要的读写蓝牙方法,应该算是简化到大家一看就可以懂了。本文完整代码可以点击此处本站下载

接下来贴出本文运行的结果,首先是连接BLE设备后,枚举出设备所有Service、Characteristic、Descriptor,并且手机会往Characteristic uuid=0000ffe1-0000-1000-8000-00805f9b34fb写入“send data->”字符串,BLE终端收到数据通过串口传到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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
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项目开发有所帮助。


分享一个集成目前主流蓝牙的demo、android蓝牙4.0开发、项目中使用到了搜索BLE终端和读写BLE终端、另外还有在程序里面开房蓝牙、在Demo里面还处理了收到BLE终端数据交互的事件、代码里都有中文注释、要读性应该非常好的、应该会对开发有一定帮助、有兴趣的哥们可以下载看看

android蓝牙4.0开发


onCreate代码

@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);
}


id="iframeu2248002_0" src="http://pos.baidu.com/uchm?rdid=2248002&dc=2&exps=112016&di=u2248002&dri=0&dis=0&dai=2&ps=1510x184&dcb=BAIDU_SSP_define&dtm=BAIDU_DUP_SETJSONADSLOT&dvi=0.0&dci=-1&dpt=none&tsr=0&tpr=1468477465848&ti=Android%E8%93%9D%E7%89%994.0%E5%BC%80%E5%8F%91_%E8%93%9D%E7%89%99%E5%BC%80%E5%8F%91%E5%AE%9E%E4%BE%8B%E6%BA%90%E7%A0%81%E4%B8%8B%E8%BD%BD_DD%E5%8D%9A%E5%AE%A2&ari=1&dbv=2&drs=1&pcs=1349x632&pss=1349x1543&cfv=0&cpl=23&chi=1&cce=true&cec=UTF-8&tlm=1468477465&rw=632&ltu=http%3A%2F%2Fdwtedx.com%2Fitshare_447.html&ltr=https%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DgbHWAKf8724p-Jh30maMxa-l_bPUOtE9ftaiwVZCe2RNf_-zKpOSxGIZ80I10MBy%26wd%3D%26eqid%3D809ab9b10002334b0000000257872a5d&ecd=1&psr=1366x768&par=1366x728&pis=-1x-1&ccd=24&cja=false&cmi=34&col=zh-CN&cdo=-1&tcn=1468477466&qn=c1f1335952e3b2e0&tt=1468477465819.159.547.548" width="120" height="45" align="center,center" vspace="0" hspace="0" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" allowtransparency="true" style="margin: 0px; padding: 0px; border-width: 0px; border-style: initial; outline: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit;font-size:undefined; line-height: inherit; font-family: inherit; word-wrap: break-word; vertical-align: bottom;">

收到BLE终端数据交互的事件

/**
 * 收到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()));
    }
};


最后给贴上Demo的源代码、希望对大家有用、有兴趣的哥们可以下载看看


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值