Android 蓝牙工具类封装:支持经典蓝牙与 BLE,兼容高版本权限
为了优化经典蓝牙(Classic Bluetooth)和低功耗蓝牙(Bluetooth Low Energy, BLE)的操作,我们可以将功能封装到一个工具类中,支持扫描、连接、通信,并兼容高版本 Android 的动态权限申请。以下是完整的工具类实现。
- 工具类功能
经典蓝牙:
扫描设备。
连接设备。
发送和接收数据。
BLE 蓝牙:
扫描设备。
连接设备。
发送和接收数据(通过 GATT 特征值)。
权限管理:
动态申请权限(包括 ACCESS_FINE_LOCATION、BLUETOOTH_SCAN、BLUETOOTH_CONNECT)。
高版本兼容:
支持 Android 12 及以上版本的权限要求。
- 工具类实现
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 的权限要求。