当前位置: 首页 > article >正文

Android 蓝牙工具类封装:支持经典蓝牙与 BLE,兼容高版本权限

为了优化经典蓝牙(Classic Bluetooth)和低功耗蓝牙(Bluetooth Low Energy, BLE)的操作,我们可以将功能封装到一个工具类中,支持扫描、连接、通信,并兼容高版本 Android 的动态权限申请。以下是完整的工具类实现。

  1. 工具类功能
    经典蓝牙:

扫描设备。

连接设备。

发送和接收数据。

BLE 蓝牙:

扫描设备。

连接设备。

发送和接收数据(通过 GATT 特征值)。

权限管理:

动态申请权限(包括 ACCESS_FINE_LOCATION、BLUETOOTH_SCAN、BLUETOOTH_CONNECT)。

高版本兼容:

支持 Android 12 及以上版本的权限要求。

  1. 工具类实现
    2.1 BluetoothHelper.java
import android.Manifest;
import android.annotation.SuppressLint;
import android.bluetooth.*;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class BluetoothHelper {

    private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); // SPP UUID
    private static final int PERMISSION_REQUEST_CODE = 100;

    private Context context;
    private BluetoothAdapter bluetoothAdapter;
    private BluetoothLeScanner bleScanner;
    private BluetoothSocket bluetoothSocket;
    private BluetoothGatt bluetoothGatt;
    private Handler handler;
    private List<BluetoothDevice> classicDevices = new ArrayList<>();
    private List<BluetoothDevice> bleDevices = new ArrayList<>();

    // BLE 相关变量
    private BluetoothGattCharacteristic writeCharacteristic;
    private BluetoothGattCharacteristic notifyCharacteristic;

    // 回调接口
    public interface ScanCallback {
        void onClassicDeviceFound(BluetoothDevice device);
        void onBleDeviceFound(BluetoothDevice device);
        void onScanFailed(String error);
    }

    public interface ConnectionCallback {
        void onConnected();
        void onConnectionFailed(String error);
    }

    public interface DataCallback {
        void onDataReceived(byte[] data);
        void onDataSent(boolean success);
    }

    public BluetoothHelper(Context context) {
        this.context = context;
        this.handler = new Handler(Looper.getMainLooper());
        this.bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (bluetoothAdapter != null) {
            this.bleScanner = bluetoothAdapter.getBluetoothLeScanner();
        }
    }

    // 检查权限
    public boolean checkPermissions() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            return ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED &&
                    ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED &&
                    ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
        } else {
            return ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
        }
    }

    // 请求权限
    public void requestPermissions() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            ActivityCompat.requestPermissions((MainActivity) context,
                    new String[]{
                            Manifest.permission.BLUETOOTH_SCAN,
                            Manifest.permission.BLUETOOTH_CONNECT,
                            Manifest.permission.ACCESS_FINE_LOCATION
                    },
                    PERMISSION_REQUEST_CODE);
        } else {
            ActivityCompat.requestPermissions((MainActivity) context,
                    new String[]{
                            Manifest.permission.ACCESS_FINE_LOCATION
                    },
                    PERMISSION_REQUEST_CODE);
        }
    }

    // 开始扫描经典蓝牙设备
    public void startClassicScan(ScanCallback callback) {
        if (!checkPermissions()) {
            callback.onScanFailed("Permissions not granted");
            return;
        }

        classicDevices.clear();
        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        context.registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                    BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    classicDevices.add(device);
                    callback.onClassicDeviceFound(device);
                }
            }
        }, filter);

        bluetoothAdapter.startDiscovery();
    }

    // 开始扫描 BLE 设备
    @SuppressLint("MissingPermission")
    public void startBleScan(ScanCallback callback) {
        if (!checkPermissions()) {
            callback.onScanFailed("Permissions not granted");
            return;
        }

        bleDevices.clear();
        bleScanner.startScan(new ScanCallback() {
            @Override
            public void onScanResult(int callbackType, ScanResult result) {
                BluetoothDevice device = result.getDevice();
                bleDevices.add(device);
                callback.onBleDeviceFound(device);
            }

            @Override
            public void onScanFailed(int errorCode) {
                callback.onScanFailed("BLE scan failed with error code: " + errorCode);
            }
        });
    }

    // 停止扫描
    @SuppressLint("MissingPermission")
    public void stopScan() {
        bluetoothAdapter.cancelDiscovery();
        if (bleScanner != null) {
            bleScanner.stopScan(null);
        }
    }

    // 连接经典蓝牙设备
    public void connectClassicDevice(BluetoothDevice device, ConnectionCallback callback) {
        new Thread(() -> {
            try {
                bluetoothSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
                bluetoothSocket.connect();
                handler.post(callback::onConnected);
            } catch (IOException e) {
                handler.post(() -> callback.onConnectionFailed(e.getMessage()));
            }
        }).start();
    }

    // 连接 BLE 设备
    @SuppressLint("MissingPermission")
    public void connectBleDevice(BluetoothDevice device, BluetoothGattCallback gattCallback) {
        bluetoothGatt = device.connectGatt(context, false, gattCallback);
    }

    // 发送数据(经典蓝牙)
    public void sendClassicData(byte[] data, DataCallback callback) {
        new Thread(() -> {
            try {
                OutputStream outputStream = bluetoothSocket.getOutputStream();
                outputStream.write(data);
                handler.post(() -> callback.onDataSent(true));
            } catch (IOException e) {
                handler.post(() -> callback.onDataSent(false));
            }
        }).start();
    }

    // 接收数据(经典蓝牙)
    public void receiveClassicData(DataCallback callback) {
        new Thread(() -> {
            try {
                InputStream inputStream = bluetoothSocket.getInputStream();
                byte[] buffer = new byte[1024];
                int bytes;
                while ((bytes = inputStream.read(buffer)) != -1) {
                    byte[] receivedData = new byte[bytes];
                    System.arraycopy(buffer, 0, receivedData, 0, bytes);
                    handler.post(() -> callback.onDataReceived(receivedData));
                }
            } catch (IOException e) {
                handler.post(() -> callback.onDataReceived(null));
            }
        }).start();
    }

    // 发送数据(BLE)
    @SuppressLint("MissingPermission")
    public void sendBleData(byte[] data, DataCallback callback) {
        if (writeCharacteristic != null && bluetoothGatt != null) {
            writeCharacteristic.setValue(data);
            bluetoothGatt.writeCharacteristic(writeCharacteristic);
            handler.post(() -> callback.onDataSent(true));
        } else {
            handler.post(() -> callback.onDataSent(false));
        }
    }

    // 启用通知以接收数据(BLE)
    @SuppressLint("MissingPermission")
    public void enableBleNotifications(BluetoothGattCharacteristic characteristic, DataCallback callback) {
        if (bluetoothGatt != null) {
            bluetoothGatt.setCharacteristicNotification(characteristic, true);
            BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
                    UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
            if (descriptor != null) {
                descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                bluetoothGatt.writeDescriptor(descriptor);
            }
        }
    }

    // 断开连接
    public void disconnect() {
        try {
            if (bluetoothSocket != null) {
                bluetoothSocket.close();
            }
            if (bluetoothGatt != null) {
                bluetoothGatt.disconnect();
                bluetoothGatt.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2.2 使用示例
扫描设备

BluetoothHelper bluetoothHelper = new BluetoothHelper(this);

// 扫描经典蓝牙设备
bluetoothHelper.startClassicScan(new BluetoothHelper.ScanCallback() {
    @Override
    public void onClassicDeviceFound(BluetoothDevice device) {
        Log.d("BluetoothHelper", "Classic Device Found: " + device.getName());
    }

    @Override
    public void onBleDeviceFound(BluetoothDevice device) {
        Log.d("BluetoothHelper", "BLE Device Found: " + device.getName());
    }

    @Override
    public void onScanFailed(String error) {
        Log.e("BluetoothHelper", "Scan Failed: " + error);
    }
});

// 扫描 BLE 设备
bluetoothHelper.startBleScan(new BluetoothHelper.ScanCallback() {
    @Override
    public void onClassicDeviceFound(BluetoothDevice device) {
        Log.d("BluetoothHelper", "Classic Device Found: " + device.getName());
    }

    @Override
    public void onBleDeviceFound(BluetoothDevice device) {
        Log.d("BluetoothHelper", "BLE Device Found: " + device.getName());
    }

    @Override
    public void onScanFailed(String error) {
        Log.e("BluetoothHelper", "Scan Failed: " + error);
    }
});

连接设备

// 连接经典蓝牙设备
bluetoothHelper.connectClassicDevice(device, new BluetoothHelper.ConnectionCallback() {
    @Override
    public void onConnected() {
        Log.d("BluetoothHelper", "Classic Device Connected");
    }

    @Override
    public void onConnectionFailed(String error) {
        Log.e("BluetoothHelper", "Connection Failed: " + error);
    }
});

// 连接 BLE 设备
bluetoothHelper.connectBleDevice(device, new BluetoothGattCallback() {
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            Log.d("BluetoothHelper", "BLE Device Connected");
        } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
            Log.d("BluetoothHelper", "BLE Device Disconnected");
        }
    }
});

经典蓝牙发送和接收数据

// 发送数据(经典蓝牙)
bluetoothHelper.sendClassicData("Hello Bluetooth".getBytes(), new BluetoothHelper.DataCallback() {
    @Override
    public void onDataReceived(byte[] data) {
        // 处理接收到的数据
    }

    @Override
    public void onDataSent(boolean success) {
        Log.d("BluetoothHelper", "Data Sent: " + success);
    }
});

// 接收数据(经典蓝牙)
bluetoothHelper.receiveClassicData(new BluetoothHelper.DataCallback() {
    @Override
    public void onDataReceived(byte[] data) {
        Log.d("BluetoothHelper", "Data Received: " + new String(data));
    }

    @Override
    public void onDataSent(boolean success) {
        // 无需处理
    }
});

BLE蓝牙发送和接收数据

// 连接 BLE 设备
bluetoothHelper.connectBleDevice(device, new BluetoothGattCallback() {
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            gatt.discoverServices();
        }
    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            BluetoothGattService service = gatt.getService(UUID.fromString("你的服务UUID"));
            if (service != null) {
                writeCharacteristic = service.getCharacteristic(UUID.fromString("写特征值UUID"));
                notifyCharacteristic = service.getCharacteristic(UUID.fromString("通知特征值UUID"));

                // 启用通知
                bluetoothHelper.enableBleNotifications(notifyCharacteristic, new BluetoothHelper.DataCallback() {
                    @Override
                    public void onDataReceived(byte[] data) {
                        Log.d("BluetoothHelper", "BLE Data Received: " + new String(data));
                    }

                    @Override
                    public void onDataSent(boolean success) {
                        // 无需处理
                    }
                });
            }
        }
    }

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        byte[] data = characteristic.getValue();
        Log.d("BluetoothHelper", "BLE Notification Data: " + new String(data));
    }
});

// 发送数据(BLE)
bluetoothHelper.sendBleData("Hello BLE".getBytes(), new BluetoothHelper.DataCallback() {
    @Override
    public void onDataReceived(byte[] data) {
        // 无需处理
    }

    @Override
    public void onDataSent(boolean success) {
        Log.d("BluetoothHelper", "BLE Data Sent: " + success);
    }
});

2.3 注意事项
权限管理:

在 Android 12 及以上版本,需要动态申请 BLUETOOTH_SCAN 和 BLUETOOTH_CONNECT 权限。

在 Android 11 及以下版本,需要动态申请 ACCESS_FINE_LOCATION 权限。

高版本兼容:

使用 @SuppressLint(“MissingPermission”) 忽略权限检查,确保在实际运行时已授予权限。

线程管理:

经典蓝牙的通信操作应在后台线程中进行,避免阻塞主线程。
通过以上工具类,你可以轻松实现经典蓝牙和 BLE 的扫描、连接和通信功能,并兼容高版本 Android 的权限要求。


http://www.kler.cn/a/582063.html

相关文章:

  • 【redis】数据类型之布隆过滤器
  • 存储过程和自定义函数在银行信贷业务中的应用(oracle)
  • Spark数据倾斜深度解析与实战解决方案
  • 力扣-数组-367 有效的完全平方数
  • 【机器学习的主要类型】
  • HTML5 + Bootstrap5 网站底部代码实现与解析
  • Django系列教程(3)——Django的MVT设计模式
  • 大语言模型(LLM)的微调与应用
  • Elasticsearch 2025/3/7
  • 【漫话机器学习系列】125.普拉托变换(Platt Scaling)
  • 【UNIAPP】获取视频的第一帧作为封面(基于视频URL,Canvas)复制即用
  • git文件过大导致gitea仓库镜像推送失败问题解决(push failed: context deadline exceeded)
  • 在VMware Workstation Pro上轻松部署CentOS7 Linux虚拟机
  • 使用Java爬虫根据关键词获取衣联网商品列表:实战指南
  • 《2025年软件测试工程师面试》MySQL面试题
  • Linux 环境变量快速上手指南
  • 《UI 设计:点亮大数据可视化的智慧之光》
  • 告别手抖烦恼,重拾生活稳 “态”
  • 【物联网-WIFI】
  • 【MySQL】增删改查进阶