Android BLE开发总结

Android BLE开发总结

基本知识

官方文档

在这里插入图片描述

在Android BLE开发中,设备、服务、特征和描述它们之间的关系如下:

  • 设备(Device):一个设置包含多个 Profile。指BLE设备,如蓝牙耳机、传感器等。
  • 配置文件(Profile):一个 Profile 包含多个 Service。
  • 服务(Service):一个 Service 包含多个 Charactoeristic。每个 Service 都有一个 UUID 唯一标识。
  • 特征(Characteristic):一个 Characteristic 包含多个 Descriptor。指BLE设备服务中的特征值,每个特征值有一个唯一的UUID,可以读取、写入和监听特征值数据。
  • 描述(Descriptor):指BLE设备服务中特征值的描述信息,描述信息通常包含对特征值的详细描述和配置信息。

开发流程

一、权限申请。

二、检查BLE是否可用。

三、开启蓝牙。

四、扫描设备。

五、建立连接。

六、发现服务。

七、监听特征。

八、发送指令接收数据。

九、断开连接和关闭释放资源。

详细操作

一、权限申请

AndroidManifest.xml注册

<!-- Android11及以下 -->
<uses-permission
                 android:name="android.permission.BLUETOOTH"
                 android:maxSdkVersion="30" />
<uses-permission
                 android:name="android.permission.BLUETOOTH_ADMIN"
                 android:maxSdkVersion="30" />

<!-- Android6及以上需要申请位置权限 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<!-- Android12及以上需要申请蓝牙扫描、广播、连接权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />


<!-- 如果您说您的应用需要该功能,那么 Google Play 商店将 在缺少这些功能的设备上向用户隐藏您的应用 -->
<uses-feature
              android:name="android.hardware.bluetooth"
              android:required="true" />
<uses-feature
              android:name="android.hardware.bluetooth_le"
              android:required="true" />

如果app不需要定位权限,可以使用 neverForLocation :

<!-- 仅当你可以强烈断言你的应用程序永远不会从蓝牙扫描结果中获取物理位置时,才包含“neverForLocation” -->

<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
                     android:usesPermissionFlags="neverForLocation" />

动态权限申请:

这里用到了 XXPermissions 权限框架。

//        List<String> mPermissionList = new ArrayList<>();
//        // Android 版本大于等于 12 时,申请新的蓝牙权限
//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
//            mPermissionList.add(Manifest.permission.BLUETOOTH_SCAN);
//            mPermissionList.add(Manifest.permission.BLUETOOTH_ADVERTISE);
//            mPermissionList.add(Manifest.permission.BLUETOOTH_CONNECT);
//            //根据实际需要申请定位权限
//            mPermissionList.add(Manifest.permission.ACCESS_COARSE_LOCATION);
//            mPermissionList.add(Manifest.permission.ACCESS_FINE_LOCATION);
//        } else {
//            //Android 6.0开始 需要定位权限
//            mPermissionList.add(Manifest.permission.ACCESS_FINE_LOCATION);
//            mPermissionList.add(Manifest.permission.ACCESS_COARSE_LOCATION);
//        }

XXPermissions.with(this)
    .permission(Permission.Group.BLUETOOTH)
    .permission(Permission.ACCESS_FINE_LOCATION, Permission.ACCESS_COARSE_LOCATION)
    .request(new OnPermissionCallback() {
        @Override
        public void onGranted(@NonNull List<String> permissions, boolean allGranted) {
            for (int i = 0; i < permissions.size(); i++) {
                Log.e("TAG", "onGranted " + i + " " + permissions.get(i));
            }
            if (allGranted) {
                // 判断定位服务是否打开
                if (!isLocationEnabled()) {
                    openLocation();
                }
            }
        }

        @Override
        public void onDenied(@NonNull List<String> permissions, boolean doNotAskAgain) {
            for (int i = 0; i < permissions.size(); i++) {
                Log.e("TAG", "onDenied " + i + " " + permissions.get(i));
            }
        }
    });

开启手机的定位服务:

/**
 * 判断定位服务是否开启,方式一:
 */
private boolean isLocationEnabled1() {
    LocationManager manager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
    //gps定位
    boolean isGpsProvider = manager.isProviderEnabled(LocationManager.GPS_PROVIDER);
    //网络定位
    boolean isNetWorkProvider = manager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
    return isGpsProvider || isNetWorkProvider;
}

/**
 * 判断定位服务是否开启,方式二:
 */
private boolean isLocationEnabled2() {
    int locationMode = 0;
    String locationProviders;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        try {
            locationMode = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.LOCATION_MODE);
        } catch (Settings.SettingNotFoundException e) {
            e.printStackTrace();
            return false;
        }
        return locationMode != Settings.Secure.LOCATION_MODE_OFF;
    } else {
        locationProviders = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED);
        return !TextUtils.isEmpty(locationProviders);
    }
}

/**
  * 打开定位服务
  */
private void openLocation() {
    Intent enableLocate = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
    startActivityForResult(enableLocate, 666);
}

二、检查BLE是否可用

/**
     * 获取蓝牙适配器
     */
private BluetoothAdapter getBluetoothAdapter() {
    //        BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
    //        BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
    //        return bluetoothAdapter;

    // 等价于如下
    return BluetoothAdapter.getDefaultAdapter();
}
/**
     * 检查设备是否支持蓝牙
     */
private boolean supporBLE() {
    if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) || getBluetoothAdapter() == null) {
        Toast.makeText(this, "当前设备不支持蓝牙", Toast.LENGTH_SHORT).show();
        return false;
    } else {
        Toast.makeText(this, "当前设备支持蓝牙", Toast.LENGTH_SHORT).show();
        return true;
    }
}

三、蓝牙是否开启

/**
     * 检查蓝牙是否开启
     */
private boolean checkEnabled() {
    boolean isEnabled = getBluetoothAdapter().isEnabled();
    Toast.makeText(this, isEnabled ? "已开启" : "未开启", Toast.LENGTH_SHORT).show();
    return isEnabled;
}
/**
     * 打开蓝牙,方式一
     */
private void openBLE1() {
    BluetoothAdapter bluetoothAdapter = getBluetoothAdapter();
    // 直接开启蓝牙,Android13不支持了
    boolean enable = bluetoothAdapter.enable();
    if (enable) {
        Toast.makeText(this, "开启蓝牙了", Toast.LENGTH_SHORT).show();
    }
}

/**
     * 打开蓝牙,方式二
     */
private void openBLE2() {
    Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(intent, REQUEST_ENABLE_BT);
}

四、扫描设备

private boolean isScanning = false;
private Handler handler = new Handler();
private static final long SCAN_PERIOD = 30_000; //扫描时间

/**
     * 开始扫描蓝牙设备
     */
private void startScan() {
    if (!isScanning) {
        Log.e("TAG", "开始扫描");
        isScanning = true;
        BluetoothLeScanner bluetoothLeScanner = getBluetoothAdapter().getBluetoothLeScanner();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                isScanning = false;
                bluetoothLeScanner.stopScan(leScanCallback);
            }
        }, SCAN_PERIOD);
        bluetoothLeScanner.startScan(leScanCallback);
    }
}

/**
     * 停止扫描蓝牙设备
     */
public void stopScan() {
    if (isScanning) {
        Log.e("TAG", "停止扫描");
        isScanning = false;
        handler.removeCallbacksAndMessages(null);
        BluetoothLeScanner bluetoothLeScanner = getBluetoothAdapter().getBluetoothLeScanner();
        bluetoothLeScanner.stopScan(leScanCallback);
    }
}

/**
     * 扫描结果回调
     */
private final ScanCallback leScanCallback = new ScanCallback() {
    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        super.onScanResult(callbackType, result);
        BluetoothDevice device = result.getDevice();
        if (device != null) {
            String deviceInfoStr = "\n" +
                "设备名:" + device.getName() + "\n" +
                "地址:" + device.getAddress() + "\n" +
                "uuids:" + Arrays.toString(device.getUuids());
            Log.e("TAG", "BLE : " + deviceInfoStr);
            if (filterDevice(device.getName())) {
                Log.e("TAG", "找到指定设备,停止扫描");
                stopScan();
                deviceType = getDeviceType(device.getName());
                deviceAddress = device.getAddress();
                viewBinding.tvDevice.setText(deviceInfoStr);
            }
        }
    }
};

/**
     * 过滤指定设备
     */
private boolean filterDevice(String deviceName) {
    return "BLE-EMP-Ui".equals(deviceName) || "Bluetooth BP".equals(deviceName);
}

五、连接设备

/**
     * 连接gatt服务
     */
public boolean connect(final String address) {
    try {
        BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
        // 连接Gatt服务,用于通信
        mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
        return true;
    } catch (IllegalArgumentException e) {
        Log.e("TAG", "连接异常");
        return false;
    }
}

监听连接状态

private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
    /**
     * 连接状态监听
     */
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        super.onConnectionStateChange(gatt, status, newState);
        Log.e("TAG", "onConnectionStateChange");
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            // 成功连接Gatt服务
            Log.e("TAG", "成功连接Gatt服务");
            connectionState = STATE_CONNECTED;
            broadcastUpdate(ACTION_GATT_CONNECTED);
            // 发现BLE提供的服务
            mBluetoothGatt.discoverServices();
        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            // 与GATT服务断开连接
            Log.e("TAG", "与GATT服务断开连接");
            connectionState = STATE_DISCONNECTED;
            broadcastUpdate(ACTION_GATT_DISCONNECTED);
            mBluetoothGatt = null;
        }
    }
}

监听蓝牙广播

/**
     * 注册蓝牙状态广播
     */
private void regStateReceiver() {
    IntentFilter filter = new IntentFilter();
    filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
    filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
    filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
    registerReceiver(mBluetoothStateReceiver, filter);
}

/**
     * 注销蓝牙状态广播
     */
private void unregStateReceiver() {
    unregisterReceiver(mBluetoothStateReceiver);
}
/**
     * 监听蓝牙状态广播
     */
private final BroadcastReceiver mBluetoothStateReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        final String action = intent.getAction();
        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
        switch (action) {
            case BluetoothDevice.ACTION_ACL_CONNECTED:
                Log.e("TAG", "蓝牙设备已连接");
                tv_bluetooth_state.setText("蓝牙设备已连接");
                break;
            case BluetoothDevice.ACTION_ACL_DISCONNECTED:
                Log.e("TAG", "蓝牙设备断开连接");
                tv_bluetooth_state.setText("蓝牙设备断开连接");
                break;
            case BluetoothAdapter.ACTION_STATE_CHANGED:
                int blueState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
                switch (blueState) {
                    case BluetoothAdapter.STATE_TURNING_ON:
                        Log.e("TAG", "手机蓝牙正在开启");
                        tv_bluetooth_state.setText("手机蓝牙正在开启");
                        break;
                    case BluetoothAdapter.STATE_ON:
                        Log.e("TAG", "手机蓝牙已开启");
                        tv_bluetooth_state.setText("手机蓝牙已开启");
                        break;
                    case BluetoothAdapter.STATE_TURNING_OFF:
                        Log.e("TAG", "手机蓝牙正在关闭");
                        tv_bluetooth_state.setText("手机蓝牙正在关闭");
                        mBluetoothLeService.disconnect();
                        break;
                    case BluetoothAdapter.STATE_OFF:
                        Log.e("TAG", "手机蓝牙已关闭");
                        tv_bluetooth_state.setText("手机蓝牙已关闭");
                        mBluetoothLeService.close();
                        break;
                }
                break;
        }
    }
};

六、发现蓝牙服务

private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {

    /**
     * 发现服务和特征,并订阅通知
     */
    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        super.onServicesDiscovered(gatt, status);
        if (status == BluetoothGatt.GATT_SUCCESS) {
            broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
            BluetoothGattService service = mBluetoothGatt.getService(UUID.fromString(SERVICE_UUID));
            characteristic = service.getCharacteristic(UUID.fromString(CHARACTERISTIC_UUID));
            // 设置订阅通知
            setCharacteristicNotification(characteristic, true);
        } else {
            Log.e("TAG", "onServicesDiscovered received: " + status);
        }
    }
}

七、订阅通知、读指令、写指令

/**
     * 读取BEL设备的特征值
     */
public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
    if (mBluetoothGatt != null && characteristic != null) {
        mBluetoothGatt.readCharacteristic(characteristic);
    }
}

/**
     * 向BLE设备写入特征值
     */
public void writeCharacteristic(BluetoothGattCharacteristic characteristic, byte[] data) {
    if (mBluetoothGatt != null && characteristic != null) {
        characteristic.setValue(data);
        mBluetoothGatt.writeCharacteristic(characteristic);
    }
}

/**
     * 订阅通知
     */
public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic, boolean enabled) {
    if (mBluetoothGatt == null) {
        return;
    }
    mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(CHARACTERISTIC_UUID));
    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
    mBluetoothGatt.writeDescriptor(descriptor);
}

八、监听数据变化

/**
         * 发送读指令监听
         */
@Override
public void onCharacteristicRead(@NonNull BluetoothGatt gatt, @NonNull BluetoothGattCharacteristic characteristic, @NonNull byte[] value, int status) {
    super.onCharacteristicRead(gatt, characteristic, value, status);
    Log.e("TAG", "onCharacteristicRead");
    if (status == BluetoothGatt.GATT_SUCCESS) {
        byte[] data = value;
        // 处理读取到的数据
        Log.e("TAG", "获取特征数据:" + BytesUtils.bytesToHexString(data));
    }
}

/**
         * 发送写指令监听
         */
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
    super.onCharacteristicWrite(gatt, characteristic, status);
    Log.e("TAG", "onCharacteristicWrite");
    Log.e("TAG", "获取写指令:" + BytesUtils.bytesToHexString(characteristic.getValue()));
    if (status == BluetoothGatt.GATT_SUCCESS) {
        Log.e("TAG", "写入成功");
        broadcastUpdate(ACTION_GATT_SERVICES_WRITE, BytesUtils.bytesToHexString(characteristic.getValue()));
    } else {
        Log.e("TAG", "写入失败");
    }
}

/**
         * 数据变化监听
         * 获取从BLE设备的数据
         */
@Override
public void onCharacteristicChanged(@NonNull BluetoothGatt gatt, @NonNull BluetoothGattCharacteristic characteristic, @NonNull byte[] value) {
    super.onCharacteristicChanged(gatt, characteristic, value);
    Log.e("TAG", "onCharacteristicChanged");
    Log.e("TAG", "从设备接收数据:" + BytesUtils.bytesToHexString(value));
    broadcastUpdate(ACTION_GATT_SERVICES_CHANGED, BytesUtils.bytesToHexString(value));
}

/**
         * 订阅成功监听
         */
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
    super.onDescriptorWrite(gatt, descriptor, status);
    Log.e("TAG", "onDescriptorWrite");
}

九、断开连接和关闭资源

mBluetoothGatt.disconnect(); //断开连接
mBluetoothGatt.close(); //关闭资源
mBluetoothGatt = null;

一、disconnect()

disconnect() 方法会断开与 BLE 设备的 GATT 连接,但仍然保持与设备的物理连接,可以使用 connectGatt() 方法重新建立 GATT 连接。

二、close()

close() 方法用于断开关闭与 BLE 设备的 GATT 连接,并释放与该设备关联的所有资源。这包括释放 BluetoothGatt 对象、清除服务缓存等。如果您想要重新连接同一台设备,则需要重新调用 connectGatt() 方法获取新的 BluetoothGatt 对象。

因此,disconnect() 方法适用于临时断开 GATT 连接,以便稍后重新连接。而 close() 方法适用于完全关闭与设备的连接并释放所有资源。

/**
     * 关闭Gatt连接
     */
private void close() {
    if (mBluetoothGatt == null) {
        return;
    }
    mBluetoothGatt.close();
    mBluetoothGatt = null;
}

重启手机蓝牙连不上问题

当重启手机蓝牙后,连不上Gatt服务,BluetoothAdapter是系统变量,重启手机的时候可能会把扫描的设备信息清理掉,这就会导致connectGatt()连接失败。因此最好的方法是重启APP后先手动扫描一次,在进行重连。

代码下载

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: Android BLE开发是指在Android平台上使用蓝牙低功耗(BLE)技术进行应用程序开发BLE是一种省电的蓝牙通信技术,被广泛应用于智能穿戴设备、医疗设备、家居设备等领域。 Android平台提供了一套完整的API来支持BLE开发开发者可以使用这些API来搜索和连接BLE设备、发送和接收数据、读取和写入BLE特征值等等。 首先,开发者需要在AndroidManifest.xml文件中添加必要的权限,如蓝牙和蓝牙管理器权限。然后,在代码中实例化一个BluetoothManager对象来获取BluetoothAdapter(蓝牙适配器)实例。 接下来,开发者可以使用BluetoothAdapter的方法来搜索和连接BLE设备。搜索时,可以通过实现BluetoothAdapter.LeScanCallback接口来获取搜索到的设备信息。连接时,可以通过实现BluetoothGattCallback接口来处理与设备的通信。 一旦成功连接到BLE设备,开发者可以使用BluetoothGatt对象来发送和接收数据。通过BluetoothGatt对象,可以发现服务和特征值,读取和写入特征值等等操作。同时,开发者也可以监控设备发出的通知和指示。 在开发过程中,开发者还需要注意BLE通信的一些特点。例如,BLE是基于事件驱动的,所以开发者需要处理相关的回调方法;BLE设备的连接是一种异步过程,所以开发者需要在连接过程中处理各种状态;BLE通信是基于GATT协议,开发者需要熟悉相关的概念和操作等。 总而言之,Android BLE开发提供了一种在Android平台上与BLE设备进行通信的方式。通过使用AndroidBLE API,开发者可以方便地实现与BLE设备的连接和数据传输,为开发各种BLE应用程序提供了便利。 ### 回答2: Android BLE开发是指在Android设备上使用BLE(蓝牙低功耗)技术进行应用开发的过程。BLE是一种蓝牙技术,相比传统的蓝牙技术具有低功耗、简单、成本低等优势,适用于低功耗设备之间的通信。 在Android BLE开发中,首先需要通过在AndroidManifest.xml文件中声明蓝牙权限来获取蓝牙访问权限。然后,需要使用BluetoothAdapter类来获取蓝牙适配器,并检查设备是否支持BLE功能。 接下来,在开发中需要使用BluetoothGatt类来建立与远程BLE设备的连接和通信。使用BluetoothGattCallback类可以监听到连接状态的改变,以及接收到的数据。 在与BLE设备通信时,需要使用GATT(通用属性配置配置文件)协议来发送和接收数据。GATT协议通过将数据分为服务(Service)和特征(Characteristic)进行管理。服务代表一个特定的功能,而特征代表服务的具体属性。 在开发过程中,还可以使用BluetoothLeScanner类进行扫描周围的BLE设备。当发现设备后,可以通过BluetoothDevice类来获取设备的详细信息,如设备名称、MAC地址等。 总结来说,Android BLE开发需要了解蓝牙低功耗技术以及相关API的使用。通过建立连接、发送数据、接收数据等操作,可以实现与BLE设备的通信。开发人员需要注意处理连接状态、数据解析等问题,以确保应用的可靠性和稳定性。 ### 回答3: Android BLE开发是指基于Android系统的蓝牙低功耗(Bluetooth Low Energy,以下简称BLE)技术进行应用开发的过程。 在Android BLE开发中,首先需要进行设备扫描。通过使用与蓝牙相关的API,我们可以搜索附近的BLE设备并获取设备的相关信息,例如设备名称、信号强度、MAC地址等。扫描到设备后,可以使用设备的唯一标识符(UUID)进行连接。 连接设备后,可以进行数据通信。BLE通信主要通过GATT(通用属性配置文件)协议进行,该协议规定了BLE设备和Android应用之间的数据传输格式和规则。开发者可以通过GATT API访问BLE设备的服务和特征,读取和写入相应的属性值。 在数据通信过程中,也可以进行数据处理。开发者可以对从BLE设备接收到的数据进行解析、处理和展示。例如,对传感器采集的数据进行分析、计算和展示,或者根据接收到的数据进行特定的操作和控制。 在开发过程中,还需要注意一些注意事项。例如,保持正确的扫描周期,避免频繁的连接和断开操作,合理处理设备不可用的情况等等。此外,对于BLE通信的兼容性,开发者应考虑不同设备的支持情况,以确保应用在各种Android设备上的正常运行。 总结来说,Android BLE开发是在Android平台上利用BLE技术进行应用开发的过程。通过设备扫描、连接和数据通信,开发者可以实现与BLE设备之间的无线数据交互。通过合理的数据处理和注意事项的考虑,可以提高应用的稳定性和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值