BluetoothAdapter هو كلاس نظام Android يمثل محول Bluetooth المحلي للجهاز. BluetoothAdapter هو نقطة الدخول لجميع عمليات Bluetooth على Android: تشغيل الراديو (enable)، مسح الأجهزة، إدارة الرؤية (setScanMode)، والحصول على معلومات المحول (getName, getAddress, getState). يتوفر الكلاس عبر BluetoothManager.getAdapter() (API 18+) أو BluetoothAdapter.getDefaultAdapter(). على الأجهزة بدون وحدة Bluetooth، يُرجع getDefaultAdapter() قيمة null. وفقًا لـ Android Developers، 2026، BluetoothAdapter هو مكون إلزامي لأي تطبيق BLE على Android، بدءًا من API 5.
الرئيسية
BluetoothAdapter يمثل محول Bluetooth الفعلي لجهاز Android. كل جهاز لديه محول واحد بالضبط (باستثناء Android Automotive مع شرائح Bluetooth متعددة، التي تستخدم BluetoothManager.getAdapterList()). يغلف BluetoothAdapter حالة الراديو: STATE_OFF (0)، STATE_TURNING_ON (1)، STATE_ON (2)، STATE_TURNING_OFF (3). يتم تتبع الحالة عبر BroadcastReceiver على ACTION_STATE_CHANGED.
الحصول على نسخة BluetoothAdapter هو الخطوة الأولى لأي تطبيق BLE على Android. الطريقة الموصى بها هي عبر BluetoothManager.getAdapter() من API 18+. البديل هو الطريقة الثابتة BluetoothAdapter.getDefaultAdapter()، التي تعمل من API 5 ولكنها أقل مرونة. كلتا الطريقتين تُرجعان null إذا كان الجهاز لا يحتوي على وحدة Bluetooth (أجهزة لوحية Wi-Fi only، المحاكي). فحص null إلزامي: يجب أن ينهي التطبيق عمله بشكل صحيح أو يعطل وظائف BLE.
// الحصول على BluetoothAdapter (موصى به)
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.content.Context
class BluetoothHelper(context: Context) {
private val bluetoothAdapter: BluetoothAdapter?
init {
// الطريقة 1: عبر BluetoothManager (API 18+)
val manager = context.getSystemService(Context.BLUETOOTH_SERVICE)
as BluetoothManager?
bluetoothAdapter = manager?.adapter
// الطريقة 2: عبر طريقة ثابتة (API 5+)
// val adapter = BluetoothAdapter.getDefaultAdapter()
// فحص null
if (bluetoothAdapter == null) {
// الجهاز لا يدعم Bluetooth
}
}
// فحص حالة Bluetooth
fun isBluetoothEnabled(): Boolean {
return bluetoothAdapter?.isEnabled == true
}
// الحصول على معلومات المحول
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")
)
}
}
يوضح كلاس BluetoothHelper الحصول على BluetoothAdapter عبر BluetoothManager مع فحص null لاحق. يتحقق isBluetoothEnabled من isEnabled — شرط إلزامي قبل أي عمليات BLE. يُرجع getAdapterInfo اسم الجهاز وعنوان MAC والحالة ووضع الرؤية. مهم: على Android 10+ (API 29+)، تُرجع خدمة النظام عنوان MAC وهمي (02:00:00:00:00:00) إذا لم يكن للتطبيق أذونات BLUETOOTH_ADMIN و ACCESS_FINE_LOCATION.
BluetoothAdapter يوفر طرقًا للتحكم في راديو Bluetooth. enable() و disable() يقومان بتشغيل وإيقاف Bluetooth. تتطلب كلتا الطريقتين إذن BLUETOOTH_ADMIN ويتم تنفيذهما بشكل غير متزامن: بعد استدعاء enable()، يبدأ النظام عملية تشغيل الراديو، ويتم تتبع الحالة عبر BroadcastReceiver مع إجراء BluetoothAdapter.ACTION_STATE_CHANGED. على Android 10+، تتطلب enable() و disable() امتياز نظام إضافي — لا يمكن للتطبيقات العادية التحكم في Bluetooth برمجيًا بدون حوار مستخدم.
getState() يُرجع الحالة الحالية للمحول: STATE_OFF (10)، STATE_TURNING_ON (11)، STATE_ON (12)، STATE_TURNING_OFF (13). getAddress() يُرجع عنوان MAC لمحول Bluetooth. على Android 6+، مطلوب ACCESS_FINE_LOCATION (أو ACCESS_COARSE_LOCATION لـ API 31+) لطلب عنوان MAC. على Android 10+، يُرجع getAddress() العنوان الثابت 02:00:00:00:00:00 — العنوان الحقيقي غير متاح عبر واجهة برمجة التطبيقات العامة.
getScanMode() يحدد وضع رؤية المحول: SCAN_MODE_NONE (غير مرئي)، SCAN_MODE_CONNECTABLE (مرئي للأجهزة المتصلة)، SCAN_MODE_CONNECTABLE_DISCOVERABLE (مرئي للجميع). وضع الرؤية محدود زمنيًا (عادة 60–300 ثانية) للأمان. يتطلب تعيين الوضع عبر setScanMode() إذن BLUETOOTH_ADMIN وإذن النظام على Android 10+.
| الطريقة | الوصف | الإذن المطلوب |
|---|---|---|
| enable() | تشغيل راديو Bluetooth | BLUETOOTH_ADMIN |
| disable() | إيقاف راديو Bluetooth | BLUETOOTH_ADMIN |
| getState() | حالة المحول الحالية | BLUETOOTH |
| getAddress() | عنوان MAC للمحول | BLUETOOTH + ACCESS_FINE_LOCATION (API 23+) |
| getScanMode() | وضع رؤية الجهاز | BLUETOOTH |
| setScanMode() | تعيين وضع الرؤية | BLUETOOTH_ADMIN |
BluetoothAdapter يدعم نوعين من المسح. يتم تشغيل المسح الكلاسيكي Bluetooth (BR/EDR) عبر startDiscovery() — يكتشف أجهزة Bluetooth من جميع الأنواع، بما في ذلك الهواتف وسماعات الرأس. يتم إرجاع النتائج عبر BroadcastReceiver مع إجراء BluetoothDevice.ACTION_FOUND. يعمل startDiscovery() لمدة 12 ثانية ويمكن إلغاؤه عن طريق استدعاء cancelDiscovery(). هذه الطريقة مهملة لـ BLE — استخدم BluetoothLeScanner.
يتم مسح BLE عبر BluetoothAdapter باستخدام الطريقة المهملة startLeScan(LeScanCallback). بدءًا من API 21، توصي Google باستخدام BluetoothLeScanner، الذي يتم الحصول عليه عبر BluetoothAdapter.getBluetoothLeScanner(). يوفر BluetoothLeScanner واجهة برمجة تطبيقات أكثر مرونة: تكوين المسح عبر ScanSettings (الوضع، نوع رد الاتصال، وضع المطابقة)، التصفية عبر ScanFilter (حسب UUID الخدمة، اسم الجهاز، عنوان MAC)، ودعم PendingIntent للمسح في الخلفية.
// واجهة برمجة تطبيقات مسح BLE القديمة (مهملة) مقابل الجديدة
import android.bluetooth.BluetoothAdapter
import android.bluetooth.le.*
class BLEScanner(private val bluetoothAdapter: BluetoothAdapter?) {
// مهمل: startLeScan (API 18+، API 21)
@Suppress("DEPRECATION")
fun legacyScan() {
bluetoothAdapter?.startLeScan { device, rssi, scanRecord ->
print("Found (LE Scan): $device.name, RSSI: $rssi")
}
}
// جديد: BluetoothLeScanner (API 21+)
fun modernScan() {
val scanner = bluetoothAdapter?.bluetoothLeScanner
?: return
// إعدادات المسح
val settings = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
.build()
// تصفية حسب الخدمة (UUID معدل ضربات القلب)
val filters = listOf(
ScanFilter.Builder()
.setServiceUuid(ParcelUuid.fromString("0000180D-0000-1000-8000-00805F9B34FB"))
.build()
)
// بدء المسح
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")
}
})
}
}
يقارن كلاس BLEScanner بين startLeScan المهمل و BluetoothLeScanner الحديث. في legacyScan، يتلقى LeScanCallback BluetoothDevice و RSSI و scanRecord الخام. في modernScan، يتم استخدام ScanSettings مع وضع LOW_LATENCY (أقصى سرعة اكتشاف) و ScanFilter للتصفية حسب UUID خدمة معدل ضربات القلب (0x180D). يوفر ScanCallback onScanResult مع كائن ScanResult يحتوي على معلومات موسعة: الاسم و RSSI وبيانات الإعلان ونوع الاتصال.
BluetoothManager هو خدمة نظام Android، تم تقديمها في API 18 (Android 4.3)، لإدارة عمليات Bluetooth. قبل API 18، كانت الطريقة الوحيدة للحصول على BluetoothAdapter هي الطريقة الثابتة getDefaultAdapter(). يوفر BluetoothManager: adapter — نسخة BluetoothAdapter، getConnectedDevices() — قائمة بالأجهزة المتصلة، getDevicesMatchingConnectionStates() — التصفية حسب الحالة. يُستخدم BluetoothManager أيضًا للحصول على BluetoothLeScanner في واجهات برمجة التطبيقات القديمة.
مزايا BluetoothManager على استدعاء BluetoothAdapter.getDefaultAdapter() مباشرة: لا يعتمد التطبيق على singleton ثابت، يحترم المدير السياق (Activity/Application)، وهو أمر مهم لسيناريوهات الحسابات المتعددة في Android Enterprise. على Android Automotive مع شرائح Bluetooth متعددة، يُرجع BluetoothManager.getAdapterList() جميع المحولات المتاحة — يُرجع BluetoothAdapter.getDefaultAdapter() المحول الأول فقط.
// استخدام BluetoothManager لـ BLE
class BLEConnection(context: Context) {
private val bluetoothManager: BluetoothManager =
context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
private val adapter: BluetoothAdapter? = bluetoothManager.adapter
// الحصول على قائمة أجهزة BLE المتصلة
fun getConnectedDevices(): List<BluetoothDevice> {
return bluetoothManager.getConnectedDevices(
BluetoothProfile.GATT
)
}
// تصفية الأجهزة حسب الحالة
fun getDevicesByState(states: IntArray): List<BluetoothDevice> {
return bluetoothManager.getDevicesMatchingConnectionStates(
BluetoothProfile.GATT, states
)
}
// فحص دعم BLE على الجهاز
fun isBLESupported(): Boolean {
return adapter != null && context.packageManager
.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)
}
// طلب تشغيل Bluetooth عبر حوار النظام
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
}
}
يستخدم كلاس BLEConnection BluetoothManager للوصول إلى BluetoothAdapter والحصول على قائمة أجهزة GATT المتصلة. يتحقق isBLESupported من وجود راديو BLE عبر PackageManager.hasSystemFeature(FEATURE_BLUETOOTH_LE) — فحص مهم للأجهزة التي تحتوي على Bluetooth Classic بدون BLE. يُظهر requestEnableBluetooth حوار النظام لتشغيل Bluetooth (ACTION_REQUEST_ENABLE)، دون الحاجة إلى إذن BLUETOOTH_ADMIN — هذه هي الطريقة القانونية الوحيدة لتشغيل Bluetooth على Android 10+ بدون تطبيق نظام.
الأذونات لـ BluetoothAdapter تطورت مع كل إصدار من Android. على Android 6–11 (API 23–30)، BLUETOOTH و BLUETOOTH_ADMIN و ACCESS_FINE_LOCATION إلزامية لمسح BLE. على Android 12+ (API 31+)، قسم Google الأذونات: تم استبدال ACCESS_FINE_LOCATION بـ BLUETOOTH_SCAN (المسح)، BLUETOOTH_CONNECT (الاتصال)، BLUETOOTH_ADVERTISE (الإعلان). لاكتشاف أجهزة BLE، BLUETOOTH_SCAN كافٍ، الموقع غير مطلوب.
جدول الأذونات حسب إصدار Android:
| العملية | API 23–30 | API 31+ |
|---|---|---|
| مسح BLE | ACCESS_FINE_LOCATION | BLUETOOTH_SCAN (بدون موقع) |
| اتصال BLE | ACCESS_FINE_LOCATION | BLUETOOTH_CONNECT |
| إعلان BLE | ACCESS_FINE_LOCATION | BLUETOOTH_ADVERTISE |
| تشغيل/إيقاف | BLUETOOTH_ADMIN | BLUETOOTH_ADMIN (النظام) |
| الحصول على عنوان MAC | ACCESS_FINE_LOCATION | BLUETOOTH_CONNECT (عنوان وهمي) |
على Android 12+، جميع أذونات Bluetooth هي أذونات وقت التشغيل — يجب طلبها في وقت التشغيل عبر ActivityResultContracts.RequestMultiplePermissions. تنتمي BLUETOOTH_SCAN و BLUETOOTH_ADVERTISE إلى مجموعة NEARBY_DEVICES، وتنتمي BLUETOOTH_CONNECT إلى نفس المجموعة. تبقى أذونات BLUETOOTH و BLUETOOTH_ADMIN في البيان للتوافق مع API < 31، ولكن بالنسبة لـ API 31+ يتم تجاهلها — تتطلب Google تحديد الأذونات الجديدة صراحة.
// طلب أذونات Bluetooth على 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
)
}
}
// فحص جميع الأذونات
fun hasPermissions(context: Context): Boolean {
return getRequiredPermissions().all { permission ->
ContextCompat.checkSelfPermission(context, permission)
== PackageManager.PERMISSION_GRANTED
}
}
}
يُرجع كلاس PermissionHelper المجموعة الصحيحة من الأذونات حسب مستوى API. على Android 12+، يتم استخدام BLUETOOTH_SCAN و BLUETOOTH_CONNECT و BLUETOOTH_ADVERTISE بدون موقع. على Android 6–11، لا يزال ACCESS_FINE_LOCATION مطلوبًا لمسح BLE. يجب على المطور مراعاة كلا السيناريوهين عند طلب الأذونات عبر ActivityResultContracts أو RxPermissions.
مثال كامل لتطبيق BLE في Kotlin يستخدم BluetoothAdapter لمسح الأجهزة BLE والاتصال بها وقراءة بياناتها. يغطي المثال فحص الأذونات والحصول على المحول والمسح عبر BluetoothLeScanner والاتصال عبر BluetoothDevice.connectGatt.
// مدير BLE كامل في 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. اكتشاف الخدمات
fun canScan(): Boolean {
return adapter?.isEnabled == true
&& scanner != null
&& PermissionHelper(context).hasPermissions(context)
}
// 2. مع مرشح
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. إيقاف المسح
fun stopScanning() {
scanner?.stopScan(object : ScanCallback() {})
}
// 4. الاتصال بجهاز 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) {
// تم العثور على الخدمات، يمكن قراءة الخصائص
}
},
BluetoothDevice.TRANSPORT_LE
)
}
// 5. تحرير الموارد
fun disconnect() {
gatt?.disconnect()
gatt?.close()
gatt = null
}
}
يوحد كلاس BLEManager دورة BLE الكاملة على Android: فحص المحول والأذونات (canScan)، المسح عبر BluetoothLeScanner مع ScanSettings (startScanning)، الاتصال عبر BluetoothDevice.connectGatt مع TRANSPORT_LE (connectToDevice)، وتحرير الموارد (disconnect). يتم تنفيذ جميع عمليات BLE على خيط واجهة المستخدم — يستدعي Android استدعاءات BluetoothGattCallback على الخيط الرئيسي. لمهام BLE المكثفة، يُوصى بنقل عمليات GATT إلى HandlerThread في الخلفية.
الأسئلة الشائعة
BluetoothAdapter هو كلاس يمثل محول Bluetooth المحلي لجهاز Android. يتم الحصول عليه عبر BluetoothManager.getAdapter() (API 18+) أو BluetoothAdapter.getDefaultAdapter(). يوفر طرقًا لتشغيل/إيقاف Bluetooth ومسح الأجهزة وإدارة الرؤية والحصول على معلومات المحول. يُرجع null على الأجهزة بدون وحدة Bluetooth.
السبب هو عدم وجود راديو Bluetooth على الجهاز. نموذجي للأجهزة اللوحية Wi-Fi only ومحاكي Android و Android TV بدون Bluetooth. تحقق من getDefaultAdapter() على null عند بدء التشغيل وقم بتعطيل وظائف BLE إذا كان المحول غائبًا. بديل هو الفحص عبر PackageManager.hasSystemFeature(FEATURE_BLUETOOTH_LE) لتحديد أكثر دقة.
BluetoothLeScanner (API 21+) هو واجهة برمجة تطبيقات حديثة لمسح BLE مع دعم ScanFilter و ScanSettings و PendingIntent. startLeScan (API 18+) هو طريقة مهملة لـ BluetoothAdapter تقبل LeScanCallback بمجموعة بيانات محدودة. توصي Google باستخدام BluetoothLeScanner لجميع المشاريع الجديدة، يسمح بالتصفية حسب UUID وتكوين وضع استهلاك الطاقة والعمل في الخلفية عبر PendingIntent.
على Android 12+ (API 31)، يتطلب مسح BLE BLUETOOTH_SCAN، والاتصال يتطلب BLUETOOTH_CONNECT، والإعلان يتطلب BLUETOOTH_ADVERTISE. لم يعد إذن الموقع ACCESS_FINE_LOCATION مطلوبًا لـ BLE. على Android 6–11، ACCESS_FINE_LOCATION مطلوب. يتم طلب جميع الأذونات في وقت التشغيل عبر ActivityResultContracts.
على Android 10+، تشغيل Bluetooth برمجيًا بدون حوار نظام متاح فقط لتطبيقات النظام بإذن BLUETOOTH_PRIVILEGED. يجب على التطبيقات العادية استخدام Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) و startActivityForResult — يؤكد المستخدم التشغيل في حوار النظام. BLUETOOTH_ADMIN في البيان لا يمنح حقوق enable() على Android 10+.
الخلاصة
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.