1.介绍
蓝牙是一种短距离的无线通信技术,可以实现固定设备、移动设备之间的数据交换。一般将蓝牙分为两大类,蓝牙3.0规范之前的版本称为传统蓝牙,蓝牙4.0规范之后的版本称为低功耗蓝牙,也就是常说的BLE(Bluetooth
Low Energy)。
本文主要讲解的是Android设备与BLE设备之间的通信,Android 从4.3版本(API Level 18)开始支持BLE通信。
2.开发流程
看图说话:
首先要判断当前的Android设备是否支持蓝牙,如果支持则再判断当前蓝牙是否处于开启状态,如果未开启则发送广播通知系统开启蓝牙,蓝牙开启后开始搜索周围的蓝牙设备,注意搜索一定要设置超时处理,搜索到指定蓝牙设备后停止搜索任务。
此时可以以列表的形式供用户选择需要连接的设备,或者内部自动连接特定的设备,连接成功后,搜索此蓝牙设备提供的服务(特性、描述符的集合),搜索完成后设置一些对应的参数,即可与蓝牙设备进行通信了。
3.相关API
看下我们在开发过程中需要用到的一些API:
* 1.BluetoothAdapter
本地蓝牙适配器,用于一些蓝牙的基本操作,比如判断蓝牙是否开启、搜索蓝牙设备等。
* 2.BluetoothDevice
蓝牙设备对象,包含一些蓝牙设备的属性,比如设备名称、mac地址等。
* 3.BluetoothProfile
一个通用的蓝牙规范,设备之间按照这个规范来收发数据。
* 4.BluetoothGatt
蓝牙通用属性协议,定义了BLE通讯的基本规则,是BluetoothProfile的实现类,Gatt是Generic Attribute
Profile的缩写,用于连接设备、搜索服务等操作。
* 5.BluetoothGattCallback
蓝牙设备连接成功后,用于回调一些操作的结果,必须连接成功后才会回调。
* 6.BluetoothGattService
蓝牙设备提供的服务,是蓝牙设备特征的集合。
* 7.BluetoothGattCharacteristic
蓝牙设备特征,是构建GATT服务的基本数据单元。
* 8.BluetoothGattDescriptor
蓝牙设备特征描述符,是对特征的额外描述。
4.代码实现
需用用到的权限:
<uses-permission android:name="android.permission.BLUETOOTH" /> <
uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
Demo中把蓝牙的相关操作写在了Service中,这样即使应用退出也不会影响蓝牙的连接,在Service中通过广播的方式通知Activity做相应的处理,看下Service:
public class BleService extends Service { private final String TAG =
BleService.class.getSimpleName();private BluetoothGatt mBluetoothGatt; // 蓝牙连接状态
private int mConnectionState = 0; // 蓝牙连接已断开 private final int
STATE_DISCONNECTED =0; // 蓝牙正在连接 private final int STATE_CONNECTING = 1; //
蓝牙已连接 private final int STATE_CONNECTED = 2; // 蓝牙已连接 public final static
String ACTION_GATT_CONNECTED ="com.yl.ble.ACTION_GATT_CONNECTED"; // 蓝牙已断开
public final static String ACTION_GATT_DISCONNECTED =
"com.yl.ble.ACTION_GATT_DISCONNECTED"; // 发现GATT服务 public final static String
ACTION_GATT_SERVICES_DISCOVERED ="com.yl.ble.ACTION_GATT_SERVICES_DISCOVERED";
// 收到蓝牙数据 public final static String ACTION_DATA_AVAILABLE =
"com.yl.ble.ACTION_DATA_AVAILABLE"; // 连接失败 public final static String
ACTION_CONNECTING_FAIL ="com.yl.ble.ACTION_CONNECTING_FAIL"; // 蓝牙数据 public
final static String EXTRA_DATA = "com.yl.ble.EXTRA_DATA"; // 服务标识 private final
UUID SERVICE_UUID = UUID.fromString("0000ace0-0000-1000-8000-00805f9b34fb"); //
特征标识(读取数据) private final UUID CHARACTERISTIC_READ_UUID = UUID.fromString(
"0000ace0-0001-1000-8000-00805f9b34fb"); // 特征标识(发送数据) private final UUID
CHARACTERISTIC_WRITE_UUID = UUID.fromString(
"0000ace0-0003-1000-8000-00805f9b34fb"); // 描述标识 private final UUID
DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); //
服务相关 private final IBinder mBinder = new LocalBinder(); public class LocalBinder
extends Binder { public BleService getService() { return BleService.this; } }
@Override public IBinder onBind(Intent intent) { return mBinder; } @Override
public boolean onUnbind(Intent intent) { release(); return super
.onUnbind(intent); }/** * 蓝牙操作回调 * 蓝牙连接状态才会回调 */ private final
BluetoothGattCallback mGattCallback =new BluetoothGattCallback() { @Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int
newState) {if (newState == BluetoothProfile.STATE_CONNECTED) { // 蓝牙已连接
mConnectionState = STATE_CONNECTED; sendBleBroadcast(ACTION_GATT_CONNECTED);//
搜索GATT服务 mBluetoothGatt.discoverServices(); } else if (newState ==
BluetoothProfile.STATE_DISCONNECTED) {// 蓝牙已断开连接 mConnectionState =
STATE_DISCONNECTED; sendBleBroadcast(ACTION_GATT_DISCONNECTED); } }@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) { // 发现GATT服务
if (status == BluetoothGatt.GATT_SUCCESS) { setBleNotification(); } } @Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {// 收到数据
sendBleBroadcast(ACTION_DATA_AVAILABLE, characteristic); } };/** * 发送通知 * *
@param action 广播Action */ private void sendBleBroadcast(String action) { Intent
intent =new Intent(action); sendBroadcast(intent); } /** * 发送通知 * * @param
action 广播Action * @param characteristic 数据 */ private void sendBleBroadcast
(String action, BluetoothGattCharacteristic characteristic) { Intent intent =new
Intent(action);if (CHARACTERISTIC_READ_UUID.equals(characteristic.getUuid()))
{ intent.putExtra(EXTRA_DATA, characteristic.getValue()); }
sendBroadcast(intent); }/** * 蓝牙连接 * * @param bluetoothAdapter BluetoothAdapter
* @param address 设备mac地址 * @return true:成功 false: */ public boolean connect
(BluetoothAdapter bluetoothAdapter, String address) {if (bluetoothAdapter ==
null || TextUtils.isEmpty(address)) { return false; } BluetoothDevice device =
bluetoothAdapter.getRemoteDevice(address);if (device == null) { return false; }
mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
mConnectionState = STATE_CONNECTING;return true; } /** * 蓝牙断开连接 */ public void
disconnect() { if (mBluetoothGatt == null) { return; }
mBluetoothGatt.disconnect(); }/** * 释放相关资源 */ public void release() { if
(mBluetoothGatt ==null) { return; } mBluetoothGatt.close(); mBluetoothGatt =
null; } /** * 设置蓝牙设备在数据改变时,通知App */ public void setBleNotification() { if
(mBluetoothGatt ==null) { sendBleBroadcast(ACTION_CONNECTING_FAIL); return; }
// 获取蓝牙设备的服务 BluetoothGattService gattService =
mBluetoothGatt.getService(SERVICE_UUID);if (gattService == null) {
sendBleBroadcast(ACTION_CONNECTING_FAIL);return; } // 获取蓝牙设备的特征
BluetoothGattCharacteristic gattCharacteristic =
gattService.getCharacteristic(CHARACTERISTIC_READ_UUID);if (gattCharacteristic
==null) { sendBleBroadcast(ACTION_CONNECTING_FAIL); return; } // 获取蓝牙设备特征的描述符
BluetoothGattDescriptor descriptor =
gattCharacteristic.getDescriptor(DESCRIPTOR_UUID);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);if
(mBluetoothGatt.writeDescriptor(descriptor)) {//
蓝牙设备在数据改变时,通知App,App在收到数据后回调onCharacteristicChanged方法
mBluetoothGatt.setCharacteristicNotification(gattCharacteristic,true); } } /**
* 发送数据 * * @param data 数据 * @return true:发送成功 false:发送失败 */ public boolean
sendData(byte[] data) { // 获取蓝牙设备的服务 BluetoothGattService gattService = null; if
(mBluetoothGatt !=null) { gattService =
mBluetoothGatt.getService(SERVICE_UUID); }if (gattService == null) { return
false; } // 获取蓝牙设备的特征 BluetoothGattCharacteristic gattCharacteristic =
gattService.getCharacteristic(CHARACTERISTIC_WRITE_UUID);if (gattCharacteristic
==null) { return false; } // 发送数据 gattCharacteristic.setValue(data); return
mBluetoothGatt.writeCharacteristic(gattCharacteristic); } }
当蓝牙连接成功后,会回调BluetoothGattCallback中的onConnectionStateChange方法,但是此时还不能与蓝牙设备进行通信,还需要调用BluetoothGatt中的discoverServices方法搜索蓝牙设备提供的服务,也就是我们上文中提到的BluetoothGattService,搜索完成后会回调BluetoothGattCallback中的onServicesDiscovered方法,这时就轮到setBleNotification方法大显身手了。
首先通过一个UUID获取到蓝牙设备提供的服务(BluetoothGattService),这个UUID是由硬件程序定义的,开发的过程中看文档就可以了,获取到服务后,再通过一个UUID获取到蓝牙设备的特征(BluetoothGattCharacteristic),然后再获取设备特征的描述符(BluetoothGattDescriptor),设置在蓝牙设备数据改变时,主动通知App,此时回调BluetoothGattCallback中的onCharacteristicChanged方法,通过characteristic.getValue()可以获取到通知的数据。
在BluetoothGattCallback还有很多回调方法,都是与BluetoothGatt的操作相对应的,很好理解,在这里就不一一举例了。
到这里Service的逻辑就介绍完了,下面来看下在Activity中如何进行处理:
public class MainActivity extends AppCompatActivity implements View.
OnClickListener { private final int REQUEST_ENABLE_BT = 1; private RecyclerView
rvDeviceList;private Button btnScan; private BluetoothAdapter mBtAdapter;
private BleService mBleService; private BroadcastReceiver mBleReceiver; private
DeviceListAdapter mDeviceListAdapter;private List<BluetoothDevice>
mBluetoothDeviceList;private List<String> mRssiList; @Override protected void
onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); rvDeviceList =
findViewById(R.id.rv_device_list); btnScan = findViewById(R.id.btn_scan);
btnScan.setOnClickListener(this); initBle(); initData(); registerBleReceiver();
}/** * 初始化蓝牙 */ private void initBle() { mBtAdapter =
BluetoothAdapter.getDefaultAdapter();if (mBtAdapter == null) { Toast.makeText(
this, "蓝牙不可用", Toast.LENGTH_LONG).show(); return; } if
(!mBtAdapter.isEnabled()) { Intent intent =new
Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(intent,
REQUEST_ENABLE_BT);return; } // 搜索蓝牙设备 scanBleDevice(); } /** * 初始化数据 */ private
void initData() { // 蓝牙设备列表 mBluetoothDeviceList = new ArrayList<>(); //
蓝牙设备RSSI列表 mRssiList = new ArrayList<>(); mDeviceListAdapter = new
DeviceListAdapter(mBluetoothDeviceList, mRssiList);
rvDeviceList.setLayoutManager(new LinearLayoutManager(this));
rvDeviceList.setAdapter(mDeviceListAdapter);// 连接蓝牙设备
mDeviceListAdapter.setOnItemClickListener(new
DeviceListAdapter.OnItemClickListener() {@Override public void onItemClick(View
view,int position) { Toast.makeText(MainActivity.this, "开始连接",
Toast.LENGTH_SHORT).show(); mBtAdapter.stopLeScan(mLeScanCallback);
mBleService.connect(mBtAdapter,
mBluetoothDeviceList.get(position).getAddress()); } }); }/** * 注册蓝牙信息接收器 */
private void registerBleReceiver() { // 绑定服务 Intent intent = new Intent(this,
BleService.class); bindService(intent, mServiceConnection,
Context.BIND_AUTO_CREATE); startService(intent);// 注册蓝牙信息广播接收器 IntentFilter
filter =new IntentFilter(); filter.addAction(BleService.ACTION_GATT_CONNECTED);
filter.addAction(BleService.ACTION_GATT_DISCONNECTED);
filter.addAction(BleService.ACTION_GATT_SERVICES_DISCOVERED);
filter.addAction(BleService.ACTION_DATA_AVAILABLE);
filter.addAction(BleService.ACTION_CONNECTING_FAIL); mBleReceiver =new
BleReceiver(); registerReceiver(mBleReceiver, filter); }/** * 搜索蓝牙设备 */ private
void scanBleDevice() { mBtAdapter.stopLeScan(mLeScanCallback);
mBtAdapter.startLeScan(mLeScanCallback);// 搜索10s new Handler().postDelayed(new
Runnable() {@Override public void run() {
mBtAdapter.stopLeScan(mLeScanCallback); } },10000); } /** * 搜索蓝牙设备回调 */ private
BluetoothAdapter.LeScanCallback mLeScanCallback =new
BluetoothAdapter.LeScanCallback() {@Override public void onLeScan
(BluetoothDevice bluetoothDevice,int i, byte[] bytes) { if
(!mBluetoothDeviceList.contains(bluetoothDevice)) {
mBluetoothDeviceList.add(bluetoothDevice); mRssiList.add(String.valueOf(i));
mDeviceListAdapter.notifyDataSetChanged(); } } };/** * 服务 */ private
ServiceConnection mServiceConnection =new ServiceConnection() { public void
onServiceConnected(ComponentName className, IBinder rawBinder) { mBleService =
((BleService.LocalBinder) rawBinder).getService(); }public void
onServiceDisconnected(ComponentName classname) { mBleService = null; } }; /** *
蓝牙信息接收器 */ private class BleReceiver extends BroadcastReceiver { @Override
public void onReceive(Context context, Intent intent) { String action =
intent.getAction();if (TextUtils.isEmpty(action)) { return; } switch (action) {
case BleService.ACTION_GATT_CONNECTED: Toast.makeText(MainActivity.this, "蓝牙已连接"
, Toast.LENGTH_SHORT).show();break; case BleService.ACTION_GATT_DISCONNECTED:
Toast.makeText(MainActivity.this, "蓝牙已断开", Toast.LENGTH_SHORT).show();
mBleService.release();break; case BleService.ACTION_CONNECTING_FAIL:
Toast.makeText(MainActivity.this, "蓝牙已断开", Toast.LENGTH_SHORT).show();
mBleService.disconnect();break; case BleService.ACTION_DATA_AVAILABLE: byte[]
data = intent.getByteArrayExtra(BleService.EXTRA_DATA); Log.i("蓝牙", "收到的数据:" +
ByteUtils.byteArrayToHexString(data));break; default: break; } } } @Override
public void onClick(View view) { switch (view.getId()) { case R.id.btn_scan: //
搜索蓝牙 // 搜索蓝牙设备 scanBleDevice(); // 初始化数据 initData(); // 注册蓝牙信息接收器
registerBleReceiver();break; default: break; } } @Override public void
onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode
!= Activity.RESULT_OK) {return; } switch (requestCode) { case REQUEST_ENABLE_BT:
// 搜索蓝牙设备 scanBleDevice(); break; default: break; } } @Override protected void
onDestroy() { super.onDestroy(); if (mBleReceiver != null) {
unregisterReceiver(mBleReceiver); mBleReceiver =null; }
unbindService(mServiceConnection); mBleService =null; } }
应用启动后首先检测当前设备蓝牙是否可用,以及蓝牙是否开启,然后开始搜索蓝牙设备,注意一定要设置搜索的超时时间,将搜索到的蓝牙设备信息存到一个集合中,搜索时间为10s。
然后绑定BleService,以便调用Service中的方法,同时再通过startService的方式开启服务,这样Activity被销毁后service依旧可以运行。
接下来再注册一个广播接收器,接收service发来的消息并做一些处理,这时在蓝牙设备列表中点击你想要连接的设备就可以正常通信了。
在产品需求中,一般应用都是长时间连接一个硬件设备,比如手环,此时就需要做一些特殊处理,比如蓝牙连接断开后主动搜索之前已经连接的设备,或者应用运行过程中手机蓝牙被关闭之后的处理等。
5.写在最后
到这里蓝牙BLE开发就介绍完了,如有错误或者遗漏的地方可以给我留言评论,谢谢!
代码已上传至GitHub,欢迎Star、Fork!
GitHub地址:https://github.com/alidili/Demos/tree/master/BleDemo
<https://github.com/alidili/Demos/tree/master/BleDemo>
本文Demo的Apk下载地址:https://github.com/alidili/Demos/raw/master/BleDemo/BleDemo.apk
<https://github.com/alidili/Demos/raw/master/BleDemo/BleDemo.apk>
热门工具 换一换