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 UUIDprivate 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() {@Overridepublic 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() {@Overridepublic void onScanResult(int callbackType, ScanResult result) {BluetoothDevice device = result.getDevice();bleDevices.add(device);callback.onBleDeviceFound(device);}@Overridepublic 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() {@Overridepublic void onClassicDeviceFound(BluetoothDevice device) {Log.d("BluetoothHelper", "Classic Device Found: " + device.getName());}@Overridepublic void onBleDeviceFound(BluetoothDevice device) {Log.d("BluetoothHelper", "BLE Device Found: " + device.getName());}@Overridepublic void onScanFailed(String error) {Log.e("BluetoothHelper", "Scan Failed: " + error);}
});// 掃描 BLE 設備
bluetoothHelper.startBleScan(new BluetoothHelper.ScanCallback() {@Overridepublic void onClassicDeviceFound(BluetoothDevice device) {Log.d("BluetoothHelper", "Classic Device Found: " + device.getName());}@Overridepublic void onBleDeviceFound(BluetoothDevice device) {Log.d("BluetoothHelper", "BLE Device Found: " + device.getName());}@Overridepublic void onScanFailed(String error) {Log.e("BluetoothHelper", "Scan Failed: " + error);}
});

連接設備

// 連接經典藍牙設備
bluetoothHelper.connectClassicDevice(device, new BluetoothHelper.ConnectionCallback() {@Overridepublic void onConnected() {Log.d("BluetoothHelper", "Classic Device Connected");}@Overridepublic void onConnectionFailed(String error) {Log.e("BluetoothHelper", "Connection Failed: " + error);}
});// 連接 BLE 設備
bluetoothHelper.connectBleDevice(device, new BluetoothGattCallback() {@Overridepublic 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() {@Overridepublic void onDataReceived(byte[] data) {// 處理接收到的數據}@Overridepublic void onDataSent(boolean success) {Log.d("BluetoothHelper", "Data Sent: " + success);}
});// 接收數據(經典藍牙)
bluetoothHelper.receiveClassicData(new BluetoothHelper.DataCallback() {@Overridepublic void onDataReceived(byte[] data) {Log.d("BluetoothHelper", "Data Received: " + new String(data));}@Overridepublic void onDataSent(boolean success) {// 無需處理}
});

BLE藍牙發送和接收數據

// 連接 BLE 設備
bluetoothHelper.connectBleDevice(device, new BluetoothGattCallback() {@Overridepublic void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {if (newState == BluetoothProfile.STATE_CONNECTED) {gatt.discoverServices();}}@Overridepublic 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() {@Overridepublic void onDataReceived(byte[] data) {Log.d("BluetoothHelper", "BLE Data Received: " + new String(data));}@Overridepublic void onDataSent(boolean success) {// 無需處理}});}}}@Overridepublic 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() {@Overridepublic void onDataReceived(byte[] data) {// 無需處理}@Overridepublic 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.pswp.cn/diannao/72940.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/72940.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/72940.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

STM32 CAN模塊原理與應用詳解

目錄 概述 一、CAN模塊核心原理 1. CAN協議基礎 2. STM32 CAN控制器結構 3. 波特率配置 二、CAN模塊配置步驟&#xff08;基于HAL庫&#xff09; 1. 初始化CAN外設 2. 配置過濾器 3. 啟動CAN通信 三、數據收發實現 1. 發送數據幀 2. 接收數據幀&#xff08;中斷方式…

PostgreSQL_安裝部署

一、Windows系統下安裝 1.下載安裝包 登錄PostgreSQL: Downloads官網&#xff1a; 選擇14.12版本&#xff0c;點擊下載&#xff1a; 2.安裝PostgrSQL14.12 雙擊exe安裝包程序&#xff0c;準備安裝&#xff1a; 選擇安裝路徑&#xff1a; 選擇想安裝的工具&#xff1a; 選擇數…

init arry的作用,可以沒有init arry嘛?(面試題)

https://bbs.kanxue.com/thread-282657.htm 對init_array段調用的方法進行Hook https://bbs.kanxue.com/thread-191092.htm init_array原理簡單說明 https://bbs.kanxue.com/thread-280135.htm frida hook init_array自吐新解 init_array 的作用&#xff0c;以及是否可以沒有 i…

藍橋杯真題0團建dfs+哈希表/鄰接表

dfs鄰接表儲存或者哈希表的運用&#xff0c;考察我們對數據的存儲 本題核心就是在求從根節點開始的兩棵樹相同的最長序列&#xff0c;首先確定用dfs進行深搜&#xff0c;對于節點的形式可以用鄰接表&#xff0c;鄰接矩陣&#xff0c;哈希表來進行存儲數據。下面看代碼 鄰接表 …

使用 AIStor、MLflow 和 KServe 將模型部署到 Kubernetes

在之前幾篇關于 MLOps 工具的文章中&#xff0c;我展示了有多少流行的 MLOps 工具跟蹤與模型訓練實驗相關的指標。我還展示了他們如何使用 MinIO 來存儲作為模型訓練管道一部分的非結構化數據。但是&#xff0c;一個好的 MLOps 工具應該做的不僅僅是管理您的實驗、數據集和模型…

kali linux web掃描工具

Kali Linux是一款專為網絡安全領域而打造的操作系統&#xff0c;提供了眾多優秀的安全工具&#xff0c;其中就包括了強大的web掃描工具。Web掃描是網絡安全檢測的一個重要環節&#xff0c;它可以幫助安全專家檢測網站的漏洞&#xff0c;提升網站的安全性。 Kali Linux中集成了…

Linux losetup循環設備

好的&#xff0c;以下是命令的中文解釋和使用步驟&#xff1a; 命令解釋&#xff1a; losetup -r /dev/loop0 /system/app.bin&#xff1a; losetup 是一個用于將文件與循環設備&#xff08;loop device&#xff09;關聯的命令。-r 選項表示將循環設備設置為只讀模式。/dev/lo…

【js逆向】

地址&#xff1a;aHR0cHM6Ly93d3cud2VpYm90b3AuY24vMi4wLw f12進入 debugger&#xff0c;過debugger 查看預覽數據 全局搜索 請求網址中的 api.weibotop.cn 在下方疑似找到了加密和解密的函數 斷點調試 控制臺輸出 那個n就是 常見的 cryptoJs庫 const cryptoJs require(cry…

1.Intel BIOS 開發指南詳細介紹

1. 引言 目的: Intel BIOS 開發指南旨在為開發者提供詳細的指導,幫助他們理解和實現 Intel 平臺上的 BIOS 功能。 適用對象: 適用于希望開發、調試和優化 BIOS 的硬件工程師、軟件工程師和系統集成商。 版本信息: 確保你使用的是最新版本的指南,以獲取最新的信息和最佳實…

deepseek在pycharm中的配置和簡單應用

對于最常用的調試python腳本開發環境pycharm&#xff0c;如何接入deepseek是我們窺探ai代碼編寫的第一步&#xff0c;熟悉起來總沒壞處。 1、官網安裝pycharm社區版&#xff08;免費&#xff09;&#xff0c;如果需要安裝專業版&#xff0c;需要另外找破解碼。 2、安裝Ollama…

【論文閱讀】多模態——LSeg

文獻基本信息 標題&#xff1a;Language-Driven Semantic Segmentation作者&#xff1a;Boyi Li、Kilian Q. Weinberger、Serge Belongie、Vladlen Koltun、Ren Ranftl單位&#xff1a;Cornell University、University of Copenhagen、Apple、Intel Labs會議/期刊&#xff1a;…

【MySQL基礎-1】MySQL 用戶管理指南:創建用戶、修改密碼與權限分配

MySQL 作為廣泛使用的關系型數據庫管理系統&#xff0c;用戶管理和權限分配是其核心功能之一。合理創建用戶、修改密碼以及分配權限&#xff0c;不僅能保障數據庫的安全性&#xff0c;還能有效控制用戶的操作范圍。本文將詳細介紹如何在 MySQL 中創建用戶、修改用戶密碼以及分配…

影刀RPA編碼版與流程版解析

影刀RPA編碼版是影刀RPA的一個高級版本&#xff0c;它結合了流程版的可視化操作和編碼版的強大靈活性&#xff0c;以下是對影刀RPA編碼版的詳細介紹&#xff1a; 1. 功能對比 流程版&#xff1a; 可視化操作&#xff1a;通過拖拽式流程設計器&#xff0c;用戶可以像搭積木一樣…

20天 - TCP 和 UDP 有什么區別?說說 TCP 的三次握手?TCP 是用來解決什么問題?

TCP 和 UDP 有什么區別&#xff1f; TCP&#xff08;傳輸控制協議&#xff09;和 UDP&#xff08;用戶數據報協議&#xff09;都是傳輸層的網絡協議&#xff0c;它們的主要區別如下&#xff1a; 連接方式 TCP&#xff1a;面向連接的協議&#xff0c;類似于打電話&#xff0c…

【MySQL_05】語法簡述(是語法,不詳細介紹各種語句)

文章目錄 一、基本規則二、標識符規則三、數據類型四、運算符五、關鍵字六、SQL 語句的通用語法結構 歷史文章點擊&#x1f449;&#xff1a;SQL &#x1f408;??github&#xff1a;https://github.com/mysql &#x1f4bb;官網&#xff1a; https://www.mysql.com &#…

JavaScript中的生成器函數詳解

在 JavaScript 中&#xff0c;生成器函數 Generator Function 是一種特殊的函數&#xff0c;它允許你在函數執行過程中暫停和恢復。生成器函數通過 function* 語法定義&#xff0c;并使用 yield 關鍵字來控制函數的執行流程。生成器函數返回一個生成器對象&#xff0c;該對象遵…

計算機網絡——交換機

一、什么是交換機&#xff1f; 交換機&#xff08;Switch&#xff09;是局域網&#xff08;LAN&#xff09;中的核心設備&#xff0c;負責在 數據鏈路層&#xff08;OSI第二層&#xff09;高效轉發數據幀。它像一位“智能交通警察”&#xff0c;根據設備的 MAC地址 精準引導數…

Git合并工具在開發中的使用指南

在團隊協作開發中&#xff0c;Git 是最常用的版本控制工具&#xff0c;而代碼合并&#xff08;Merge&#xff09;是多人協作不可避免的環節。當多個開發者同時修改同一文件的相同區域時&#xff0c;Git 無法自動完成合并&#xff0c;此時需要借助合并工具&#xff08;Merge Too…

實現多語言適配

1.在res下創建多語言資源文件&#xff1a; 2.選擇需要的語言 然后得到多種語言適配string文件&#xff1a; 3.代碼設置多語言 object LanguageHelper {/*** 獲取適配的 Context*/fun getAttachBaseContext(context: Context): Context {return if (Build.VERSION.SDK_INT > …

【學習方法一】

學習方法一 一、通用高效學習法二、學科專項方法三、工具與技術輔助四、習慣與心理策略五、避免常見誤區總結六、進階學習策略七、解決學習痛點八、場景化學習法九、資源與工具推薦十、個性化學習調整十一、長期學習心態十二、常見問題QA十三、應對特殊挑戰的學習法十四、健康與…