Android BLE开发

本文详细介绍蓝牙BLE技术在Android 6.0+版本的应用适配,包括如何判断BLE支持情况、检查权限、蓝牙设备的扫描与连接、数据读写及通知设置、连接关闭等关键步骤,并提供了实用的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:

好几年前做了一个体温监测的手环App,前些日子给我反馈说很多客户都连不上设备了。我想是时候做安卓6.0+的适配了。

适配过程遇到各种问题,所以写下这编文章以记录。

最简单理解蓝牙:

主要几个东西:

BluetoothDevice

BluetoothGattService

BluetoothGattCharacteristic

BluetoothGattDescriptor

BluetoothGatt

1、BluetoothDevice下可能有多个BluetoothGattService,BluetoothGattService下可能有多个BluetoothGattCharacteristic,BluetoothGattCharacteristic下又可能有多个BluetoothGattDescriptor。它们都有一个UUID标识

2、连接成功后各种操作都用BluetoothGatt,获取BluetoothGattService等

主要流程:

1、判断是否支持BLE

2、判断权限

3、打开蓝牙并扫描设备

4、连接

5、读写和设置通知(如果设备主动发信息,可以通过notification的方式,否则要轮询地读设备数据)

6、关闭连接

下面我们都按这个流程来实现

1、判断是否支持BLE

    @Override
    public boolean isSupportBle() {
        return mContext.getApplicationContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
    }

2、检查权限,如果是6.0+要加入

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
 if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.M) {
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
                    || ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, MY_PERMISSIONS_REQUEST_LOCATION);
            }
        } 

这里有坑,如果用户授权了定位权限,但是手机的位置信息没有打开也扫描不到设备,日志也没有看出任何问题。

检查是否打开位置信息:

    public static  boolean isLocationEnable(Context context) {
        LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
        boolean networkProvider = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
        boolean gpsProvider = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
        if (networkProvider || gpsProvider) {
            return true;
        }
        return false;
    }

跳到打开位置信息设置页面

    private void setLocationService() {
        try {
            Intent locationIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
            this.startActivityForResult(locationIntent, REQUEST_CODE_LOCATION_SETTINGS);
        }catch (Exception mE){
            
        }
    }

3、打开蓝牙并扫描设备

    if (isSupportBle()) {
            mBluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
            mBluetoothAdapter = mBluetoothManager.getAdapter();
            if (null != mBluetoothAdapter) {
               boolean enable = mBluetoothAdapter.enable();
            }
        }

扫描设备,是新版本中boolean startLeScan(LeScanCallback callback)已经过时用void startScan(final ScanCallback callback)替代。

 
 @Override
    public boolean scan(final ScanCallback listener) {
        boolean success = false;
        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN_MR2 || null == mBluetoothManager) {
            Log.e(TAG, "android.os.Build.VERSION.SDK_INT is lower than 18  or not support Ble");
        } else if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
            BluetoothLeScanner bluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
            bluetoothLeScanner.startScan(new NewBaseScanCallback(listener));
            Log.e(TAG, "bluetoothLeScanner.startScan");
            success = true;
        } else {
            success = mBluetoothAdapter.startLeScan(new BaseScanCallback(listener));
            Log.e(TAG, "bluetoothAdapter.startLeScan(callback)--");
        }
        return success;
    }

 4、连接设备

    @Override
    public BluetoothGatt connect(BluetoothDevice device, BluetoothGattCallback mGattCallback) {
        mBluetoothGatt = device.connectGatt(mContext, true, mGattCallback);
        if (mBluetoothGatt == null) {
            Log.e(TAG, "Trying to create a new connection.---false");
        }
        return mBluetoothGatt;
    }
    private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            if (newState == BluetoothGatt.STATE_CONNECTED) {
                gatt.discoverServices();
                updateState(IBle.STATE_CONNECTED);
            } else if (newState == BluetoothGatt.STATE_DISCONNECTED) {
                updateState(IBle.STATE_DISCONNECTED);
            } else if (newState == BluetoothGatt.STATE_CONNECTING) {
                updateState(IBle.STATE_CONNECTING);
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);
            updateState(IBle.STATE_SERVICES_DISCOVERED);
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);
            //进行接收数据的相关操作
            final byte[] data = characteristic.getValue();
            if (data != null && data.length > 0) {
                if (null != mBleCallback) {
                    (mBleCallback).getData(data);
                }

            }

        }
    };

收到连接成功的回调后调用 gatt.discoverServices();还要等到void onServicesDiscovered(BluetoothGatt gatt, int status)回调的时候才可做操作。这时候才算真正意思的连接成功。

5、读写和设置通知

    @Override
    public boolean readAlertLevel(UUID service_uuid, UUID read_uuid) {
        boolean status = false;
        if (null == mBluetoothGatt) {
            return status;
        }
        for (BluetoothGattService bluetoothGattService : mBluetoothGatt.getServices()) {
            Log.e(TAG, "BluetoothGattService-writeLlsAlertLevel--mBluetoothGatt.getServices()--" + bluetoothGattService.getUuid());
        }
        BluetoothGattService linkLossService = mBluetoothGatt.getService(service_uuid);
        if (linkLossService == null) {
            Log.e(TAG, "mBluetoothGatt.getService(SERVIE_UUID)---link loss Alert service not found!");
            return status;
        }
        BluetoothGattCharacteristic alertLevel = null;
        alertLevel = linkLossService.getCharacteristic(read_uuid);
        if (alertLevel == null) {
            Log.e(TAG, "link loss Alert Level charateristic not found!");
            return status;
        }
        status = mBluetoothGatt.readCharacteristic(alertLevel);
        Log.e(TAG, "readLlsAlertLevel() - status=" + status);
        return status;
    }

    @Override
    public boolean writeAlertLevel(int iAlertLevel, UUID service_uuid, UUID write_uuid, byte[] bb) {
        boolean status = false;
        if (null == mBluetoothGatt) {
            return status;
        }
        for (BluetoothGattService bluetoothGattService : mBluetoothGatt.getServices()) {
            Log.e(TAG, "BluetoothGattService-writeLlsAlertLevel--mBluetoothGatt.getServices()--" + bluetoothGattService.getUuid());
        }
        BluetoothGattService linkLossService = mBluetoothGatt.getService(service_uuid);
        if (linkLossService == null) {
            Log.e(TAG, "mBluetoothGatt.getService(SERVIE_UUID)---link loss Alert service not found!");
            return status;
        }
        BluetoothGattCharacteristic alertLevel = null;
        alertLevel = linkLossService.getCharacteristic(write_uuid);
        if (alertLevel == null) {
            Log.e(TAG, "link loss Alert Level charateristic not found!");
            return status;
        }

        int storedLevel = alertLevel.getWriteType();
        Log.e(TAG, "storedLevel() - storedLevel=" + storedLevel);
        alertLevel.setValue(bb);
        Log.e("发送的指令", "bb" + bb[0]);
        alertLevel.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
        status = mBluetoothGatt.writeCharacteristic(alertLevel);
        Log.e(TAG, "writeLlsAlertLevel() - status=" + status);
        return status;
    }

    @Override
    public boolean setNotify(UUID service_uuid, UUID read_uuid) {
        if (null == getBluetoothGatt()) {
            return false;
        }
        BluetoothGattService service = getBluetoothGatt().getService(service_uuid);
        if (service == null) {
            return false;
        }
        BluetoothGattCharacteristic  characteristic = service.getCharacteristic(read_uuid);
        if (null == characteristic) {
            return false;
        }
        int properties = characteristic.getProperties();
        if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == 0) {
            Log.e(TAG, "Check characteristic property: false");
            return false;
        }
        boolean success = getBluetoothGatt().setCharacteristicNotification(characteristic, true);
        Log.e(TAG, "setCharacteristicNotification: "+success);
        BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(UUID_CLIENT_CHARACTERISTIC_CONFIG_DESCRIPTOR));
        if (descriptor != null) {
            descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
             getBluetoothGatt().writeDescriptor(descriptor);
        }
       return success;
}
    

6、关闭连接

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

最后把扫描与连接的简单封装了一下提供了一个非常简单的扫描连接方法

/**
 * 扫描设备自动连接
 *
 * @param timeout          超时时间,扫描不到自动停止
 * @param mScanInterceptor 扫描到的设备判断是否符合条件,自动连接
 */
public void scanAndConnect(long timeout, ScanInterceptor mScanInterceptor) 

demo代码https://github.com/YangKee/Ble

有不对的地方欢迎批评指正,谢谢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值