BluetoothAdapter là một lớp hệ thống Android đại diện cho bộ điều hợp Bluetooth cục bộ của thiết bị. BluetoothAdapter là điểm vào cho tất cả các thao tác Bluetooth trên Android: bật radio (enable), quét thiết bị, quản lý khả năng hiển thị (setScanMode), lấy thông tin bộ điều hợp (getName, getAddress, getState). Lớp này có sẵn qua BluetoothManager.getAdapter() (API 18+) hoặc BluetoothAdapter.getDefaultAdapter(). Trên các thiết bị không có mô-đun Bluetooth, getDefaultAdapter() trả về null. Theo Android Developers, 2026, BluetoothAdapter là thành phần bắt buộc cho bất kỳ ứng dụng BLE nào trên Android, bắt đầu từ API 5.
Những điểm chính
BluetoothAdapter đại diện cho bộ điều hợp Bluetooth vật lý của thiết bị Android. Mỗi thiết bị có chính xác một bộ điều hợp (ngoại trừ Android Automotive với nhiều chip Bluetooth, sử dụng BluetoothManager.getAdapterList()). BluetoothAdapter đóng gói trạng thái radio: STATE_OFF (0), STATE_TURNING_ON (1), STATE_ON (2), STATE_TURNING_OFF (3). Trạng thái được theo dõi qua BroadcastReceiver trên ACTION_STATE_CHANGED.
Lấy phiên bản BluetoothAdapter là bước đầu tiên của bất kỳ ứng dụng BLE nào trên Android. Phương pháp được khuyến nghị là qua BluetoothManager.getAdapter() từ API 18+. Phương pháp thay thế là phương thức tĩnh BluetoothAdapter.getDefaultAdapter(), hoạt động từ API 5 nhưng kém linh hoạt hơn. Cả hai phương thức đều trả về null nếu thiết bị không có mô-đun Bluetooth (máy tính bảng chỉ Wi-Fi, trình giả lập). Kiểm tra null là bắt buộc: ứng dụng phải kết thúc đúng cách hoặc tắt các chức năng BLE.
// Lấy BluetoothAdapter (khuyến nghị)
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.content.Context
class BluetoothHelper(context: Context) {
private val bluetoothAdapter: BluetoothAdapter?
init {
// Cách 1: qua BluetoothManager (API 18+)
val manager = context.getSystemService(Context.BLUETOOTH_SERVICE)
as BluetoothManager?
bluetoothAdapter = manager?.adapter
// Cách 2: qua phương thức tĩnh (API 5+)
// val adapter = BluetoothAdapter.getDefaultAdapter()
// Kiểm tra null
if (bluetoothAdapter == null) {
// Thiết bị không hỗ trợ Bluetooth
}
}
// Kiểm tra trạng thái Bluetooth
fun isBluetoothEnabled(): Boolean {
return bluetoothAdapter?.isEnabled == true
}
// Lấy thông tin bộ điều hợp
fun getAdapterInfo(): Map<String, String> {
return mapOf(
"name" to (bluetoothAdapter?.name ?: "N/A"),
"address" to (bluetoothAdapter?.address ?: "N/A"),
"state" to (bluetoothAdapter?.state?.toString() ?: "N/A"),
"scanMode" to (bluetoothAdapter?.scanMode?.toString() ?: "N/A")
)
}
}
Lớp BluetoothHelper minh họa cách lấy BluetoothAdapter qua BluetoothManager với kiểm tra null sau đó. isBluetoothEnabled kiểm tra isEnabled — một điều kiện bắt buộc trước bất kỳ thao tác BLE nào. getAdapterInfo trả về tên thiết bị, địa chỉ MAC, trạng thái và chế độ hiển thị. Quan trọng: trên Android 10+ (API 29+), dịch vụ hệ thống trả về địa chỉ MAC giả (02:00:00:00:00:00) nếu ứng dụng không có quyền BLUETOOTH_ADMIN và ACCESS_FINE_LOCATION.
BluetoothAdapter cung cấp các phương thức điều khiển radio Bluetooth. enable() và disable() bật và tắt Bluetooth. Cả hai phương thức đều yêu cầu quyền BLUETOOTH_ADMIN và thực thi bất đồng bộ: sau khi gọi enable(), hệ thống bắt đầu quá trình bật radio và trạng thái được theo dõi qua BroadcastReceiver với hành động BluetoothAdapter.ACTION_STATE_CHANGED. Trên Android 10+, enable() và disable() yêu cầu đặc quyền hệ thống bổ sung — các ứng dụng thông thường không thể điều khiển Bluetooth bằng chương trình mà không có hộp thoại người dùng.
getState() trả về trạng thái hiện tại của bộ điều hợp: STATE_OFF (10), STATE_TURNING_ON (11), STATE_ON (12), STATE_TURNING_OFF (13). getAddress() trả về địa chỉ MAC của bộ điều hợp Bluetooth. Trên Android 6+, cần ACCESS_FINE_LOCATION (hoặc ACCESS_COARSE_LOCATION cho API 31+) để yêu cầu địa chỉ MAC. Trên Android 10+, getAddress() trả về địa chỉ hằng số 02:00:00:00:00:00 — địa chỉ thực không có sẵn qua API công khai.
getScanMode() xác định chế độ hiển thị của bộ điều hợp: SCAN_MODE_NONE (vô hình), SCAN_MODE_CONNECTABLE (hiển thị với thiết bị đã kết nối), SCAN_MODE_CONNECTABLE_DISCOVERABLE (hiển thị với tất cả). Chế độ hiển thị bị giới hạn thời gian (thường 60–300 giây) vì lý do bảo mật. Đặt chế độ qua setScanMode() yêu cầu BLUETOOTH_ADMIN và quyền hệ thống trên Android 10+.
| Phương thức | Mô tả | Quyền cần thiết |
|---|---|---|
| enable() | Bật radio Bluetooth | BLUETOOTH_ADMIN |
| disable() | Tắt radio Bluetooth | BLUETOOTH_ADMIN |
| getState() | Trạng thái hiện tại của bộ điều hợp | BLUETOOTH |
| getAddress() | Địa chỉ MAC của bộ điều hợp | BLUETOOTH + ACCESS_FINE_LOCATION (API 23+) |
| getScanMode() | Chế độ hiển thị của thiết bị | BLUETOOTH |
| setScanMode() | Đặt chế độ hiển thị | BLUETOOTH_ADMIN |
BluetoothAdapter hỗ trợ hai loại quét. Quét Bluetooth cổ điển (BR/EDR) được khởi chạy qua startDiscovery() — phát hiện thiết bị Bluetooth ở tất cả các loại, bao gồm điện thoại và tai nghe. Kết quả được trả về qua BroadcastReceiver với hành động BluetoothDevice.ACTION_FOUND. startDiscovery() chạy trong 12 giây và có thể bị hủy bằng cách gọi cancelDiscovery(). Phương thức này không được khuyến khích cho BLE — hãy sử dụng BluetoothLeScanner.
Quét BLE qua BluetoothAdapter sử dụng phương thức không được khuyến khích startLeScan(LeScanCallback). Bắt đầu từ API 21, Google khuyến nghị sử dụng BluetoothLeScanner, được lấy qua BluetoothAdapter.getBluetoothLeScanner(). BluetoothLeScanner cung cấp API linh hoạt hơn: cấu hình quét qua ScanSettings (chế độ, loại callback, chế độ so khớp), lọc qua ScanFilter (theo UUID dịch vụ, tên thiết bị, địa chỉ MAC) và hỗ trợ PendingIntent cho quét nền.
// API quét BLE cũ (không được khuyến khích) so với mới
import android.bluetooth.BluetoothAdapter
import android.bluetooth.le.*
class BLEScanner(private val bluetoothAdapter: BluetoothAdapter?) {
// KHÔNG ĐƯỢC KHUYẾN KHÍCH: startLeScan (API 18+, API 21)
@Suppress("DEPRECATION")
fun legacyScan() {
bluetoothAdapter?.startLeScan { device, rssi, scanRecord ->
print("Found (LE Scan): $device.name, RSSI: $rssi")
}
}
// MỚI: BluetoothLeScanner (API 21+)
fun modernScan() {
val scanner = bluetoothAdapter?.bluetoothLeScanner
?: return
// Cài đặt quét
val settings = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
.build()
// Lọc theo dịch vụ (UUID nhịp tim)
val filters = listOf(
ScanFilter.Builder()
.setServiceUuid(ParcelUuid.fromString("0000180D-0000-1000-8000-00805F9B34FB"))
.build()
)
// Bắt đầu quét
scanner.startScan(filters, settings, object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
val device = result.device
val rssi = result.rssi
print("Found (BLE Scanner): ${device.name}, RSSI: $rssi, address: ${device.address}")
}
override fun onScanFailed(errorCode: Int) {
print("Scan error: $errorCode")
}
})
}
}
Lớp BLEScanner so sánh startLeScan không được khuyến khích và BluetoothLeScanner hiện đại. Trong legacyScan, LeScanCallback nhận BluetoothDevice, RSSI và scanRecord thô. Trong modernScan, ScanSettings với chế độ LOW_LATENCY (tốc độ phát hiện tối đa) và ScanFilter để lọc theo UUID dịch vụ nhịp tim (0x180D) được sử dụng. ScanCallback cung cấp onScanResult với đối tượng ScanResult chứa thông tin mở rộng: tên, RSSI, dữ liệu quảng cáo, loại kết nối.
BluetoothManager là một dịch vụ hệ thống Android, được giới thiệu trong API 18 (Android 4.3), để quản lý các thao tác Bluetooth. Trước API 18, cách duy nhất để lấy BluetoothAdapter là phương thức tĩnh getDefaultAdapter(). BluetoothManager cung cấp: adapter — một phiên bản BluetoothAdapter, getConnectedDevices() — danh sách thiết bị đã kết nối, getDevicesMatchingConnectionStates() — lọc theo trạng thái. BluetoothManager cũng được sử dụng để lấy BluetoothLeScanner trên các API cũ.
Lợi ích của BluetoothManager so với gọi trực tiếp BluetoothAdapter.getDefaultAdapter(): ứng dụng không phụ thuộc vào singleton tĩnh, trình quản lý tôn trọng ngữ cảnh (Activity/Application), điều quan trọng cho các kịch bản đa tài khoản Android Enterprise. Trên Android Automotive với nhiều chip Bluetooth, BluetoothManager.getAdapterList() trả về tất cả các bộ điều hợp có sẵn — BluetoothAdapter.getDefaultAdapter() chỉ trả về bộ đầu tiên.
// Sử dụng BluetoothManager cho BLE
class BLEConnection(context: Context) {
private val bluetoothManager: BluetoothManager =
context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
private val adapter: BluetoothAdapter? = bluetoothManager.adapter
// Lấy danh sách thiết bị BLE đã kết nối
fun getConnectedDevices(): List<BluetoothDevice> {
return bluetoothManager.getConnectedDevices(
BluetoothProfile.GATT
)
}
// Lọc thiết bị theo trạng thái
fun getDevicesByState(states: IntArray): List<BluetoothDevice> {
return bluetoothManager.getDevicesMatchingConnectionStates(
BluetoothProfile.GATT, states
)
}
// Kiểm tra hỗ trợ BLE trên thiết bị
fun isBLESupported(): Boolean {
return adapter != null && context.packageManager
.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)
}
// Yêu cầu bật Bluetooth qua hộp thoại hệ thống
fun requestEnableBluetooth(activity: MainActivity) {
if (adapter?.isEnabled == false) {
val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
activity.startActivityForResult(intent, REQUEST_ENABLE_BT)
}
}
companion object {
const val REQUEST_ENABLE_BT = 1001
}
}
Lớp BLEConnection sử dụng BluetoothManager để truy cập BluetoothAdapter và lấy danh sách thiết bị GATT đã kết nối. isBLESupported kiểm tra sự hiện diện của radio BLE qua PackageManager.hasSystemFeature(FEATURE_BLUETOOTH_LE) — một kiểm tra quan trọng cho thiết bị có Bluetooth Classic nhưng không có BLE. requestEnableBluetooth hiển thị hộp thoại hệ thống bật Bluetooth (ACTION_REQUEST_ENABLE), không yêu cầu quyền BLUETOOTH_ADMIN — đây là cách hợp pháp duy nhất để bật Bluetooth trên Android 10+ mà không cần ứng dụng hệ thống.
Quyền cho BluetoothAdapter đã phát triển qua từng phiên bản Android. Trên Android 6–11 (API 23–30), BLUETOOTH, BLUETOOTH_ADMIN và ACCESS_FINE_LOCATION là bắt buộc cho quét BLE. Trên Android 12+ (API 31+), Google đã phân chia quyền: ACCESS_FINE_LOCATION được thay thế bằng BLUETOOTH_SCAN (quét), BLUETOOTH_CONNECT (kết nối), BLUETOOTH_ADVERTISE (quảng cáo). Để phát hiện thiết bị BLE, BLUETOOTH_SCAN là đủ, vị trí không được yêu cầu.
Bảng quyền theo phiên bản Android:
| Thao tác | API 23–30 | API 31+ |
|---|---|---|
| Quét BLE | ACCESS_FINE_LOCATION | BLUETOOTH_SCAN (không cần vị trí) |
| Kết nối BLE | ACCESS_FINE_LOCATION | BLUETOOTH_CONNECT |
| Quảng cáo BLE | ACCESS_FINE_LOCATION | BLUETOOTH_ADVERTISE |
| Bật/tắt | BLUETOOTH_ADMIN | BLUETOOTH_ADMIN (hệ thống) |
| Lấy địa chỉ MAC | ACCESS_FINE_LOCATION | BLUETOOTH_CONNECT (địa chỉ giả) |
Trên Android 12+, tất cả quyền Bluetooth đều là quyền thời gian chạy — chúng phải được yêu cầu tại thời gian chạy qua ActivityResultContracts.RequestMultiplePermissions. BLUETOOTH_SCAN và BLUETOOTH_ADVERTISE thuộc nhóm NEARBY_DEVICES, BLUETOOTH_CONNECT thuộc cùng nhóm. Quyền BLUETOOTH và BLUETOOTH_ADMIN vẫn nằm trong tệp kê khai để tương thích với API < 31, nhưng đối với API 31+ chúng bị bỏ qua — Google yêu cầu chỉ định rõ ràng các quyền mới.
// Yêu cầu quyền Bluetooth trên Android 12+
import android.Manifest
import android.content.pm.PackageManager
import android.os.Build
import androidx.core.content.ContextCompat
class PermissionHelper(context: Context) {
fun getRequiredPermissions(): Array<String> {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// Android 12+: BLE-
arrayOf(
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.BLUETOOTH_ADVERTISE
)
} else {
// Android 6-11: BLE
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.BLUETOOTH,
Manifest.permission.BLUETOOTH_ADMIN
)
}
}
// Kiểm tra tất cả quyền
fun hasPermissions(context: Context): Boolean {
return getRequiredPermissions().all { permission ->
ContextCompat.checkSelfPermission(context, permission)
== PackageManager.PERMISSION_GRANTED
}
}
}
Lớp PermissionHelper trả về bộ quyền chính xác tùy thuộc vào cấp API. Trên Android 12+, BLUETOOTH_SCAN, BLUETOOTH_CONNECT, BLUETOOTH_ADVERTISE không cần vị trí được sử dụng. Trên Android 6–11, ACCESS_FINE_LOCATION vẫn được yêu cầu cho quét BLE. Nhà phát triển cần tính đến cả hai kịch bản khi yêu cầu quyền qua ActivityResultContracts hoặc RxPermissions.
Ví dụ đầy đủ về một ứng dụng BLE trong Kotlin sử dụng BluetoothAdapter để quét, kết nối và đọc dữ liệu từ thiết bị BLE. Ví dụ bao gồm kiểm tra quyền, lấy bộ điều hợp, quét qua BluetoothLeScanner và kết nối qua BluetoothDevice.connectGatt.
// Trình quản lý BLE đầy đủ trong Kotlin
class BLEManager(private val context: Context) {
private val bluetoothManager: BluetoothManager =
context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
private val adapter: BluetoothAdapter? = bluetoothManager.adapter
private var scanner: BluetoothLeScanner? = adapter?.bluetoothLeScanner
private var gatt: BluetoothGatt? = null
// 1. Khám phá dịch vụ
fun canScan(): Boolean {
return adapter?.isEnabled == true
&& scanner != null
&& PermissionHelper(context).hasPermissions(context)
}
// 2. có bộ lọc
fun startScanning(callback: (BluetoothDevice, Int) -> Unit) {
if (!canScan()) return
val settings = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setReportDelay(0)
.build()
scanner?.startScan(null, settings, object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
callback(result.device, result.rssi)
}
})
}
// 3. Dừng quét
fun stopScanning() {
scanner?.stopScan(object : ScanCallback() {})
}
// 4. Kết nối với thiết bị BLE
fun connectToDevice(device: BluetoothDevice) {
if (adapter?.isEnabled != true) return
gatt = device.connectGatt(
context,
false,
object : BluetoothGattCallback() {
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.discoverServices()
}
}
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
// Đã tìm thấy dịch vụ, có thể đọc đặc tính
}
},
BluetoothDevice.TRANSPORT_LE
)
}
// 5. Giải phóng tài nguyên
fun disconnect() {
gatt?.disconnect()
gatt?.close()
gatt = null
}
}
Lớp BLEManager hợp nhất chu trình BLE đầy đủ trên Android: kiểm tra bộ điều hợp và quyền (canScan), quét qua BluetoothLeScanner với ScanSettings (startScanning), kết nối qua BluetoothDevice.connectGatt với TRANSPORT_LE (connectToDevice) và giải phóng tài nguyên (disconnect). Tất cả các thao tác BLE được thực thi trên luồng giao diện người dùng — Android gọi callback BluetoothGattCallback trên luồng chính. Đối với các tác vụ BLE quan trọng về hiệu suất, nên chuyển các thao tác GATT sang HandlerThread nền.
Các câu hỏi thường gặp
BluetoothAdapter là một lớp đại diện cho bộ điều hợp Bluetooth cục bộ của thiết bị Android. Nó được lấy qua BluetoothManager.getAdapter() (API 18+) hoặc BluetoothAdapter.getDefaultAdapter(). Nó cung cấp các phương thức bật/tắt Bluetooth, quét thiết bị, quản lý khả năng hiển thị và lấy thông tin bộ điều hợp. Trả về null trên thiết bị không có mô-đun Bluetooth.
Lý do là thiết bị không có radio Bluetooth. Điển hình cho máy tính bảng chỉ Wi-Fi, trình giả lập Android và Android TV không có Bluetooth. Kiểm tra getDefaultAdapter() có null khi khởi động ứng dụng và tắt các chức năng BLE nếu bộ điều hợp không có. Một phương pháp thay thế là kiểm tra qua PackageManager.hasSystemFeature(FEATURE_BLUETOOTH_LE) để phát hiện chính xác hơn.
BluetoothLeScanner (API 21+) là API hiện đại cho quét BLE với hỗ trợ ScanFilter, ScanSettings và PendingIntent. startLeScan (API 18+) là phương thức BluetoothAdapter không được khuyến khích chấp nhận LeScanCallback với tập dữ liệu hạn chế. Google khuyến nghị BluetoothLeScanner cho tất cả các dự án mới, cho phép lọc theo UUID, cấu hình chế độ tiêu thụ điện năng và hoạt động nền qua PendingIntent.
Trên Android 12+ (API 31), quét BLE yêu cầu BLUETOOTH_SCAN, kết nối yêu cầu BLUETOOTH_CONNECT và quảng cáo yêu cầu BLUETOOTH_ADVERTISE. Quyền vị trí ACCESS_FINE_LOCATION không còn được yêu cầu cho BLE. Trên Android 6–11, ACCESS_FINE_LOCATION được yêu cầu. Tất cả quyền được yêu cầu tại thời gian chạy qua ActivityResultContracts.
Trên Android 10+, bật Bluetooth bằng chương trình mà không cần hộp thoại hệ thống chỉ khả dụng cho ứng dụng hệ thống có quyền BLUETOOTH_PRIVILEGED. Ứng dụng thông thường phải sử dụng Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) và startActivityForResult — người dùng xác nhận việc bật trong hộp thoại hệ thống. BLUETOOTH_ADMIN trong tệp kê khai không cấp quyền enable() trên Android 10+.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm