Android——Bluetooth 蓝牙4.0开发

蓝牙 4.0
蓝牙4.0集成了传统蓝牙和低功耗蓝牙两个标准,所以蓝牙4.0有双模和单模之分。双模即传统蓝牙部分+低功耗蓝牙部分,单模即是单纯的低功耗蓝牙部分(BLE)。

蓝牙操作流程
蓝牙开发之前需要在 AndroidManifest.xml 中申请蓝牙相关权限

    <!-- 蓝牙相关权限 -->
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-feature
        android:name="android.hardware.bluetooth.le"
        android:required="true" />
    <!-- Android 6.0 需要申请位置权限 -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

蓝牙操作基本可以分为五步:

打开蓝牙
扫描设备
连接设备
控制设备
接收数据
Android BLE 相关功能及 API
说明
服务:每个蓝牙设备,都有多个服务,每个服务都有不同的作用,我们可以根据蓝牙协议提供的 service uuid 找出 相应的service。
特征值:蓝牙数据传输的载体。每个服务里包含多个特征值,每个特征值都有自己的特性(读、写或通知等)。
1、判断蓝牙是否打开
bluetoothAdapter.isEnabled(),true 蓝牙已打开;false 蓝牙未打开,需要跳转到设置页面打开蓝牙。
跳转蓝牙设置页面,打开蓝牙

Intent intent = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
startActivity(intent);
2、获取 bluetoothAdapter 并扫描蓝牙
蓝牙打开后,我们首先需要获取蓝牙适配器 bluetoothAdapter,获取蓝牙适配器后,就可以进行蓝牙扫描操作。

获取 bluetoothAdapter
    public void openBle() {
        if (null != mBtAdapter) {
            return;
        }
        BluetoothManager manager = (BluetoothManager) mContext.getSystemService(BLUETOOTH_SERVICE);
        if (null != manager) {
            mBtAdapter = manager.getAdapter();
        }
        if (null == mBtAdapter) {
            mBtAdapter = BluetoothAdapter.getDefaultAdapter();
        }
        //设备不支持蓝牙功能
        if (mBtAdapter == null) {
            Toast.makeText(mContext, "设备不支持蓝牙功能!", Toast.LENGTH_LONG).show();
        }
    }

蓝牙扫描 
在 Android 6.0 以后扫描蓝牙需要有位置权限,否则扫描不到附近的设备。
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                mBtAdapter.startLeScan(mLeScanCallback);
            } else {
                //需要 sdk 版本在 21(Android 5.0) 以上才可以定义使用
                mBtAdapter.getBluetoothLeScanner().startScan(mScanCallback);

            }
           //bluetoothAdapter.startDiscovery(); //该方法不建议使用

3、扫描 BLE 返回
蓝牙扫描开始后,就会有设备进行返回,返回的数据会出现重复,需要我们根据自身需求进行去重操作,并显示在列表中。

扫描结果有三种:

广播接收扫描结果(BroadcastReceiver)
⚠️ 需要调用bluetoothAdapter.startDiscovery() 方法进行扫描,并且注册 ACTION_FOUND 广播。

Android 5.0 以下API
    private BluetoothAdapter.LeScanCallback mLeScanCallback =
            new BluetoothAdapter.LeScanCallback() {

                @Override
                public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
                        //蓝牙扫描回调
                        mCallBackResult.onScanResult(scanResult);
                    }
                }
            };


⚠️ 需要使用 mBtAdapter.startLeScan(mLeScanCallback);进行扫描

Android 5.0 及以上( sdk 为 21 以上)
                mScanCallback = new ScanCallback() {
                    @Override
                    public void onScanResult(int callbackType, android.bluetooth.le.ScanResult result) {
                        super.onScanResult(callbackType, result);
                        scanType = SCAN_RESULT_TYPE_TWO;
                        // sdk 版本在 21(Android 5.0) 以上扫描结果处理
                        mCallBackResult.onScanResult(scanResult);
                    }

                    @Override
                    public void onBatchScanResults(List<android.bluetooth.le.ScanResult> results) {
                        super.onBatchScanResults(results);
                    }

                    @Override
                    public void onScanFailed(int errorCode) {
                        super.onScanFailed(errorCode);
                    }
                };

⚠️ 需要使用mBtAdapter.getBluetoothLeScanner().startScan(mScanCallback); 进行蓝牙扫描

4、设备连接
扫描到我们所需要的设备后,下一步就要进行设备连接。连接设备可以对扫描到的设备 BluetoothDevice 进行连接,也可以根据 macAddress 进行连接(其实也是根据macAddress 找到设备 BluetoothDevice 进行连接)

    public boolean connect(final String address) {
        if (mBluetoothAdapter == null || address == null) {
            Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
            return false;
        }

        if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress) && mBluetoothGatt != null) {
            Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
            mBluetoothGatt.disconnect();
            mBluetoothGatt.close();
        }

        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
        if (device == null) {
            Log.w(TAG, "Device not found.  Unable to connect.");
            return false;
        }
        //设备连接
        mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
        mBluetoothDeviceAddress = address;
        return true;
    }

以上的代码中提到了 BluetoothGatt 和 BluetoothGattCallback。

BluetoothGatt:它是操作 BLE 的主要类,蓝牙的连接、断开、发现蓝牙服务、注册通道、发送命令等都会用到它。
BluetoothGattCallback 是关于蓝牙操作的回调,里面包含了连接是否成功回调、服务发现回调、蓝牙返回数据回调、写命令成功回调等十二个回调方法。
5、蓝牙操作
向蓝牙写入命令 
向蓝牙写入命令,特征值必须有“写”的特性,才允许向蓝牙发送控制指令。

    public void writeRXCharacteristic(byte[] value) {

    Log.w("---Write","byte[] is "+ Arrays.toString(value)+ "\nHex is "+BleUtils.bytes2HexString(value));

    if(mBluetoothGatt == null)
    {
        System.out.println("=================================================================");
    }
    // 根据 UUID 获取蓝牙服务
    BluetoothGattService RxService = mBluetoothGatt.getService(RX_SERVICE_UUID);
    if (RxService == null) {
        showMessage("Rx service not found!");
        return;
    }
    // 根据 UUID 获取“写”的特征值
    BluetoothGattCharacteristic RxChar = RxService.getCharacteristic(RX_CHAR_UUID);
    if (RxChar == null) {
        showMessage("Rx charateristic not found!");
        return;
    }
    //给特征值 赋值(向蓝牙传递的命令,十六进制转 byte 数组)
    RxChar.setValue(value);
    //蓝牙写操作
    boolean status = mBluetoothGatt.writeCharacteristic(RxChar);
}

注册“读”特性特征值 
只能向有“读”特性的特征值注册读 操作

public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
    if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) !=0) {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.readCharacteristic(characteristic);
    }
}

6、断开连接和关闭蓝牙
断开蓝牙连接

public void disconnect() {
    if (mBluetoothAdapter == null || mBluetoothGatt == null) {
        Log.w(TAG, "BluetoothAdapter not initialized");
        return;
    }
    mBluetoothGatt.disconnect();
}

关闭蓝牙操作

    public void close() {
    if (mBluetoothGatt == null) {
        return;
    }
    Log.w(TAG, "mBluetoothGatt closed");
    mBluetoothDeviceAddress = null;
    mBluetoothGatt.close();
    mBluetoothGatt = null;
}

7、停止扫描
根据扫描方式的不同,停止扫描的方法也是不同:
/**
 * 停止扫描
 */
public void stopScan() {
    switch (scanType) {
        case SCAN_RESULT_TYPE_ONE:
            mBtAdapter.stopLeScan(mLeScanCallback);//Android 5.0 以下(sdk 21以下)
            break;
        case SCAN_RESULT_TYPE_TWO:
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                //Android 5.0 及以上(sdk 21以上)
                mBtAdapter.getBluetoothLeScanner().stopScan(mScanCallback);
            }
            break;
        case SCAN_RESULT_TYPE_THREE:
            //使用 bluetoothAdapter.startDiscovery() 方法扫描设备
            mBtAdapter.cancelDiscovery();
            break;
        default:
            break;
    }
}
8、BluetoothGattCallback 回调方法
onConnectionStateChange(BluetoothGatt gatt, int status, int newState)

设备连接状态回调。当 newState == BluetoothGatt.STATE_CONNECTED 时代表设备了解成功,可以进行服务扫描了 mBluetoothGatt.discoverServices()。

当 newState == BluetoothGatt.STATE_DISCONNECTED 时,代表设备断开连接。

onServicesDiscovered(BluetoothGatt gatt, int status)、

发现蓝牙服务回调。可以进行特征值筛选,通道注册等操作。其中可以根据特征值的特性进行特征值注册和筛选。例如:BluetoothGattCharacteristic 的property值 等于 0x02 则只有“读”的特性;等于 0x04 为“写”的特性,但是不回调;等于 0x08 是“写”的特性,能够回调等。(详情参考BluetoothGattCharacteristic 类)

以下是注册读取通道的方法:

 public void enableTXNotification()
{

    if (mBluetoothGatt == null) {
        showMessage("mBluetoothGatt null" + mBluetoothGatt);
        return;
    }
    //根据 UUID 获取蓝牙服务
    BluetoothGattService RxService = mBluetoothGatt.getService(RX_SERVICE_UUID);
    if (RxService == null) {
        showMessage("Rx service not found!");
        return;
    }
    //根据 UUID 获取“读”特征值
    BluetoothGattCharacteristic TxChar = RxService.getCharacteristic(TX_CHAR_UUID);
    if (TxChar == null) {
        showMessage("Tx charateristic not found!");
        return;
    }
    //注册通知
    mBluetoothGatt.setCharacteristicNotification(TxChar,true);
     // 对特征值进行描述
    BluetoothGattDescriptor descriptor = TxChar.getDescriptor(CCCD);
    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
    mBluetoothGatt.writeDescriptor(descriptor);
}
onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) 
蓝牙设备数据返回回调
onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) 
读操作的回调。一般情况下不会使用该方法
onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) 
写操作回调
总结
在蓝牙开发过程中,主要是弄清蓝牙协议,每个蓝牙设备的协议是不一样的,握手方式也是各不相同。对于向蓝牙发送命令要格外注意,稍有不慎命令编码就会出错。对于蓝牙返回的数据,也是多种多样,我们要分别处理。最后,由于蓝牙开发很多都是异步操作,为了节省Activity的开销,最好蓝牙相关操作放到service内进行。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值