BLE实践总结

BLE实践总结

最近在公司参与了一个手机客户端与智能手表通过BLE连接通信的项目,感觉BLE挺有意思。总结一下。

第一步,声明权限##

BLE是低功耗蓝牙的缩写,需要在工程的AndroidManifest.xml文件中声明如下权限:

<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>

第二步 判断系统版本号

从android 4.3开始支持BLE,所以,最好在写代码的时候判断一下系统版本号,如下:

if (android.os.Build.VERSION.SDK_INT < 18) {
    // 不支持
}

第四步 判断设备是否支持

有的手机暂时还不知是BLE或者蓝牙是否开启,需要进行代码判断,这些判断应该放在应用刚初始化的时候就进行,代码如下:
1、BLE支持性判断:

if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)){
	//不支持BLE,直接退出
	finish();
}

2、判断蓝牙是否开启
不打开蓝牙是没法进行BLE连接的。
(1)首先,获取系统的蓝牙管理器BluetoothManager,以及系统的蓝牙适配器BluetoothAdapter

BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context
                .BLUETOOTH_SERVICE);
BluetoothAdapter mBluetoothAdapter = bluetoothManager.getAdapter();

(2)然后,进行判断

if (mBluetoothAdapter == null) {
//这里表示手机不支持蓝牙
    finish();
    return;
}
if(!mBluetoothAdapter.isEnabled()){
//表示手机支持蓝牙,但是此时蓝牙不可用,未开启,需要用户开启
startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE), 0);//跳转界面
//mBluetoothAdapter.enable(); // 直接不用跳转界面,也不用用户手动开启,但是这种做法不太推荐使用
}

然后,就可以监听蓝牙开启的广播,或者等用户开启了蓝牙返回了原来的Activity,再进行蓝牙开启后的操作代码。比如在onActivityResult中判断。

第五步 获取已经绑定过的设备

上一次绑定过的BLE设备,系统会帮助我们保存起来,我们在这一次就可以获取这些绑定过的设备,代码如下

Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices();

然后,我们可以使用ListView等进行展示。

第六步 准备完毕,开始扫描周围的设备

扫描设备,系统也为我们你提供了专门的API,
需要注意的是,扫描前,我们最好执行停止扫描的操作,防止重复扫描导致异常。

mBluetoothAdapter.stopLeScan(mLeScanCallback);
mBluetoothAdapter.startLeScan(mLeScanCallback);//开始扫描
//mLeScanCallback是一个扫描过程中的回调接口,具体实现如下:
BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
        //每扫描到一个设备,就执行一次
        @Override
        public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
            //这里的device参数就是扫描到的设备对象,我们可以获取到有关于此设备的所有信息
            //rssi参数代表信号强度
            //scanRecord广播数据
            //我们可以在这里面进行ListView适配与显示
            mLeDeviceListAdapter.addDevice(device, mRssi);
            mLeDeviceListAdapter.notifyDataSetChanged();
        }
    };

蓝牙扫描过程是一个非常耗电的操作,我们不能一直让其扫描下去,需要适时的停止扫描,例如,我们希望蓝牙扫描10S后自动停止扫描,这个时候,可以借助Handler发送一个延迟消息实现,它在startLeScan方法之后执行即可,如下:

// Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);//系统提供的API
                }
            }, 10000);

当然,我们也需要在Activity的生命周期中判断蓝牙扫描是否停止,比如在Ondestory方法中判断,如果蓝牙正在扫描,停止扫描即可。
扫描完成,我们就可以选择某一个设备进行连接。

第七步 连接设备

选中设备,进行连接操作。(这时候我们仍然需要注意,也要进行停止扫描工作)。连接操作一般是通过设备的MAC地址进行连接,MAC地址我们可以通过上一步扫描到的BluetoothDevice对象获取到,如下:

mBluetoothAdapter.stopLeScan(mLeScanCallback);//停止扫描
device.getAddress();//获取设备的mac地址

然后,有了Mac地址,剩下的就是使用系统提供的API来进行连接操作:

//下面这句通过mac地址获取蓝牙设备对象,其实,通过扫描到的设备对象,也是可以的
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
if (null == device) {
    //Device not found.  Unable to connect.
    return false;
}
//开始连接
//第一个参数 context代表上下文对象
//第二个参数 Whether to directly connect to the remote device (false) or to automatically   //connect as soon as the remote device becomes available (true).
//第三个参数是一个连接回调
BluetoothGatt mBluetoothGatt = device.connectGatt(context, false, mGattCallback);
private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {

	@Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
    //发现服务回调
        super.onServicesDiscovered(gatt, status);
        if (status != BluetoothGatt.GATT_SUCCESS) { 
        //发现服务失败
            return;
        }
        //发现服务成功
        //接下来,就需要获取服务
        List<BluetoothGattService> services = mBluetoothGatt.getServices();
        for (BluetoothGattService gattService : services) {
        //遍历我们获取到的服务
            uuid = gattService.getUuid().toString();
            Log.i(TAG, "found service: " + uuid);
            //这里是为了根据UUID获取我们关心的服务BluetoothGattService
            if (UUIDStr.SERVICE_UUID.equals(uuid)) {
            //在我们关心的服务中,获取所有的蓝牙设备的特征,它是手机和BLE设备通信的关键。
                List<BluetoothGattCharacteristic> gattCharacteristics = gattService
                        .getCharacteristics();
                if (null != gattCharacteristics) {
                    // available Characteristics.
                    for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {//遍历,根据UUID获取我们关心的BluetoothGattCharacteristic
                        uuid = gattCharacteristic.getUuid().toString();
                        Log.i(TAG, "found characteristic: " + uuid);
                        if (UUIDStr.TX_UUID.equals(uuid)) {
                        //发信息
                            mTxCharacteristic = gattCharacteristic;
                            Log.i(TAG, "txCharacteristic(write) ready");
                        } else if (UUIDStr.RX_UUID.equals(uuid)) {
                        //读信息
                            mRxCharacteristic = gattCharacteristic;
                            setCharacteristicNotification(true);
                            Log.i(TAG, "rxCharacteristic(notify) ready");
                        }
                    }
                } else {
                    Log.e(TAG, "no characteristic found!");
                }
            }
        }
    }
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, final int status, final int newState) {
    //BLE连接状态发生变化时
        if (newState == BluetoothProfile.STATE_CONNECTED) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    gatt.requestMtu(MTU_SIZE);
                } else {
                    //去发现服务,onServicesDiscovered调用,之后onServicesDiscovered就会回调
                    Log.i(TAG, "discoverServices: " + mBluetoothGatt.discoverServices());
                }
                // Log.i(TAG, "discoverServices: " + mBluetoothGatt.discoverServices());
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                // release resources
                disconnRelease();
                Log.i(TAG, "Disconnected from GATT server. Gatt state: " + status);
            } else if (newState == BluetoothProfile.STATE_CONNECTING) {
                Log.i(TAG, "Connecting to GATT server.");
            } else if (newState == BluetoothProfile.STATE_DISCONNECTING) {
                Log.i(TAG, "Disconnecting from GATT server.");
            }
        
    }

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        super.onCharacteristicChanged(gatt, characteristic);
        //BLE设备主动向手机发送的数据时收到的数据回调
        characteristic.getValue(); //手机端获取接收到的数据
    }
    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
    //手机端发送数据
        if (status != BluetoothGatt.GATT_SUCCESS) {  
            return;
        }
    }
};

BluetoothGatt对象,通俗的讲,就是Android手机和BLE设备通信的一个管道。两者所有的通信都基于这个通道。它是GATT profile的封装,是一种协议规范。有了这种基于GATT协议规范的通道,两者就可以进行通信。
BluetoothGattService:蓝牙设备的服务,在这里我们把BluetoothGattService比喻成班级。每台BLE终端设备拥有多个服务,各个服务之间通过UUID(唯一标识符)区别。
BluetoothGattCharacteristic: 蓝牙设备所拥有的特征,它是手机与BLE终端设备交换数据的关键,我们做的所有事情,目的就是为了得到它。在这里我们把它比喻成学生,各个特征之间通过UUID(唯一标识符)区别。
还有一点需要注意,手机向BLE设备发送信息,没问题。但是BLE设备主动向手机发送消息就需要进行一些代码的设置:

private void setCharacteristicNotification(boolean enabled) {
	if (0 != (charact.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY)) {
	// 查看是否带有可通知属性notify  
    mBluetoothGatt.setCharacteristicNotification(charact, true);
    BluetoothGattDescriptor descriptor = charact.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")); // 这包数据什么意思,可以不用管,反正是固定这包数据就是了。
    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
    mBluetoothGatt.writeDescriptor(descriptor);
	} else if (0 != (charact.getProperties() &&    BluetoothGattCharacteristic.PROPERTY_INDICATE)) {  // 查看是否带有indecation属性
	    mBluetoothGatt.setCharacteristicNotification(charact, true);
	    BluetoothGattDescriptor descriptor =  charact.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
	    descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
	    mBluetoothGatt.writeDescriptor(descriptor);
	}
}

第八步 通信

通过上面的7步,我们终于完成了BLE通信的准备工作,正常情况下,手机和BLE设备已经完成连接,等待数据的发送或者接收。手机端发送数据可以使用系统提供的API:

//在这里设置要发送的数据内容
mTxCharacteristic.setValue(data);
if (null != mTxCharacteristic && null != mBluetoothGatt) {
//正式发送到远方设备
boolean blnWriteSuccess = mBluetoothGatt.writeCharacteristic(mTxCharacteristic);

接收消息,我们就在第7步的BluetoothGattCallback 对象中的onCharacteristicChanged回调方法中接收data,然后进行解析即可。

第九步 断开BLE连接

在某种情况下,我们还是需要断开BLE连接。代码如下:

mBluetoothGatt.disconnect();//关闭连接
mBluetoothGatt.close();//关闭客户端
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值