מפתחי אפליקציות BLE נתקלים לעתים קרובות בבעיות: מכשיר לא מתגלה, החיבור נופל לאחר כמה שניות, או שאנדרואיד מבקש פתאום גישה למיקום. שורש הבעיה הוא טיפול לא נכון במצבי המתאם וסינון לפי UUID של שירות. במשך יותר מ-5 שנים יישמנו יותר מ-30 פרויקטים מסחריים של BLE ופיתחנו גישה חזקה שמבטיחה גילוי וחיבור יציבים של מכשירים עם צריכת חשמל מינימלית. בואו נפרק את זה באמצעות דוגמאות Swift ו-Kotlin.
למה חשוב לטפל במצבי המתאם?
ב-iOS, חיפוש מכשירים אפשרי רק כאשר CBCentralManager.state == .poweredOn. יש לטפל במצבים אחרים כדי שמשתמשים יבינו את המצב:
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
startScanning()
case .poweredOff:
showAlert("Включите Bluetooth в настройках")
case .unauthorized:
if CBCentralManager.authorization == .denied {
showSettingsLink()
}
case .unsupported:
showAlert("Устройство не поддерживает Bluetooth LE")
case .resetting:
// стек перезагружается, ждём .poweredOn
break
default:
break
}
}טעות אופיינית: לא לטפל ב-func centralManagerDidUpdateState(_ central: CBCentralManager) { switch central.state { case .poweredOn: startScanning() case .poweredOff: showAlert("Включите Bluetooth в настройках") case .unauthorized: if CBCentralManager.authorization == .denied { showSettingsLink() } case .unsupported: showAlert("Устройство не поддерживает Bluetooth LE") case .resetting: // стек перезагружается, ждём .poweredOn break default: break } } ב-iOS 13 ומעלה, מה שגורם לקריסת האפליקציה. באנדרואיד המצב מורכב יותר – נדרשים בדיקות הרשאות דינמיות ותצורה נכונה של ה-manifest כדי להימנע מבקשת מיקום. לדוגמה, הוספת .unauthorized ל-android:usesPermissionFlags="neverForLocation" מאפשרת לעקוף את <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />. ללא דגל זה, המשתמש רואה בקשת מיקום גם כשהיא לא נחוצה לסריקת BLE. חסכו מהמשתמש שלב מיותר.
איך להגדיר סינון מכשירי BLE?
סינון לפי UUID של שירות הוא הליבה של גילוי מכשירים יעיל. העבירו מערך של UUIDs ל-ACCESS_FINE_LOCATION ב-iOS או ל-scanForPeripherals(withServices:) באנדרואיד:
let serviceUUIDs = [CBUUID(string: "YOUR-SERVICE-UUID")]
centralManager.scanForPeripherals(withServices: serviceUUIDs, options: [
CBCentralManagerScanOptionAllowDuplicatesKey: false
])שימוש ב-ScanFilter סורק את כל מכשירי ה-BLE הקרובים – נוח לפיתוח אבל לא לייצור: זה צורך יותר סוללה ומעמיס את הרשימה בגאדג'טים לא ידועים. סינון UUID מפחית את מספר המכשירים שהתגלו פי 3–5 בהשוואה לסריקה לא מסוננת.
באנדרואיד, השתמשו ב-let serviceUUIDs = [CBUUID(string: "YOUR-SERVICE-UUID")] centralManager.scanForPeripherals(withServices: serviceUUIDs, options: [ CBCentralManagerScanOptionAllowDuplicatesKey: false ]) :
val filters = listOf(
ScanFilter.Builder()
.setServiceUuid(ParcelUuid(UUID.fromString("YOUR-SERVICE-UUID")))
.build()
)חשוב: באנדרואיד 8 ומעלה אפשר להשתמש ב-withServices: nil לסינון לפי שם, אבל זה פחות אמין כי אפשר לשנות את השם.
השוואת מצבי סריקה באנדרואיד
| מצב | תדירות | צריכת סוללה | מתי להשתמש |
|---|---|---|---|
ScanFilter.Builder().setServiceUuid() |
~512 אלפיות השנייה | מינימלית | חיפוש ברקע |
val filters = listOf( ScanFilter.Builder() .setServiceUuid(ParcelUuid(UUID.fromString("YOUR-SERVICE-UUID"))) .build() ) |
~512 אלפיות השנייה / ~1.5 שניות | בינונית | ברירת מחדל |
setDeviceName() |
רציף | גבוהה | חיפוש פעיל בממשק המשתמש |
SCAN_MODE_LOW_POWER |
רק כאשר סורק אחר פעיל | ללא | ניטור פסיבי |
SCAN_MODE_BALANCED מגלה מכשירים פי 3 מהר יותר מ-SCAN_MODE_LOW_LATENCY אבל צורך 40% יותר אנרגיה. לכן, השתמשו ב-SCAN_MODE_OPPORTUNISTIC למסך הממשק וב-SCAN_MODE_LOW_LATENCY לרקע. במקרה של לקוח עם צמיד כושר, מעבר מ-SCAN_MODE_BALANCED ל-LOW_LATENCY הפחית את צריכת החשמל ב-18% ללא עלייה ניכרת בזמן הגילוי. שינוי זה חסך ללקוח כ-$500 בשנה בעלויות החלפת סוללות. אופטימיזציות כאלה יכולות לחסוך לארגונים $2,000–$15,000 בשנה בעלויות תחזוקת מכשירים ותמיכה למשתמשים.
איך להתחבר מחדש למכשיר ידוע?
אם ה-UUID של המכשיר ההיקפי נשמר (לדוגמה, ב-LOW_POWER), אפשר לשחזר את האובייקט ללא סריקה חוזרת:
let knownUUID = UUID(uuidString: savedUUIDString)!
let peripherals = centralManager.retrievePeripherals(withIdentifiers: [knownUUID])
if let peripheral = peripherals.first {
centralManager.connect(peripheral, options: nil)
} else {
// UUID устарел или устройство заменено — запускаем полное сканирование
startScanning()
}חיבור מחדש באמצעות LOW_LATENCY מהיר ב-30–50% מאשר סריקה חוזרת, ומקצר את זמן החיבור ב-1–2 שניות. זה קריטי לאפליקציות שמתחברות לעתים קרובות לאותו מכשיר (צמיד, חיישן).
באנדרואיד, השתמשו ב-BALANCED עם UserDefaults לחיבור מחדש. שמרו את כתובת ה-MAC או המזהה של המכשיר מ-let knownUUID = UUID(uuidString: savedUUIDString)! let peripherals = centralManager.retrievePeripherals(withIdentifiers: [knownUUID]) if let peripheral = peripherals.first { centralManager.connect(peripheral, options: nil) } else { // UUID устарел или устройство заменено — запускаем полное сканирование startScanning() } . שימו לב שבאנדרואיד 10 ומעלה, אין צורך בסריקה לחיבור מחדש אם המכשיר היה מורשה (bonded) או מחובר בעבר.
איך להימנע משגיאות חיבור נפוצות?
| שגיאה | סיבה | פתרון |
|---|---|---|
| החיבור נופל לאחר כמה שניות | חוסר בלוגיקת חיבור מחדש אוטומטי | יישמו backoff אקספוננציאלי (1 שנייה, 2 שניות, 4 שניות, 8 שניות) ב-callback של retrievePeripherals |
| האפליקציה קורסת כשמתחברים למכשיר שכבר מחובר | אין בדיקה של connectGatt |
לפני autoConnect = true, ודאו את BluetoothDevice |
| דליפות משאבים באנדרואיד | לא מטפלים בכשל של centralManager(_:didDisconnectPeripheral:error:) |
תמיד קראו ל-connect על peripheral.state == .disconnected במקרי כשל |
חיבור מחדש אוטומטי עם backoff אקספוננציאלי מפחית מקרי ניתוק ב-90% בתרחישים אמיתיים. לדוגמה, בפרויקט של מכשור רפואי, יישום תבנית זו צמצם את פניות התמיכה ב-200 שעות בשנה.
התהליך שלנו לשילוב BLE
- ניתוח דרישות – קביעת שירותים ומאפיינים נדרשים, תדירות החלפת נתונים, מכשירים נתמכים.
- עיצוב – בחירת ארכיטקטורה (תפקיד מרכזי או היקפי), אסטרטגיית חיבור מחדש, ניהול הרשאות.
- יישום – כתיבת קוד סריקה וחיבור עם טיפול מלא במצבים והרשאות. השתמשו ב-DI לבדיקות קלות יותר.
- בדיקות – בדיקות על יותר מ-15 דגמי מכשירים כולל גרסאות מערכת הפעלה ישנות, הדמיית ניתוקים ואובדן אות.
- תיעוד ותמיכה – מסירת חומרי אינטגרציה, ייעוץ לצוות.
תוצרי העבודה (Что входит в работу)
אנו מיישמים סריקה וחיבור במפתח מלא. כולל:
- קוד Swift/Kotlin עם טיפול מלא במצבי מתאם והרשאות.
- סינון UUID של שירות וטיפול בכפילויות.
- חיבור מחדש אוטומטי בעת ניתוק.
- תיעוד מלא של אינטגרציית BLE.
- גישה לקוד המקור ולמאגר פרטי.
- הדרכה אחת למפתחים.
- 3 חודשי תמיכה לאחר השחרור.
- אינטגרציה עם האפליקציה שלכם (ארכיטקטורה, DI).
- ייעוץ ותיקונים לאחר השחרור.
יישמנו יותר מ-30 פרויקטים מסחריים של BLE. צרו קשר כדי להעריך את הפרויקט שלכם. הניסיון שלנו: יותר מ-5 שנים בפיתוח BLE. אנו מבטיחים חיבור יציב ועמידה בהנחיות App Store ו-Google Play. העלות האופיינית ליישום מודול BLE מתחילה מ-$5,000, עם חיסכון של עד $15,000 בשנה בעלויות תחזוקת מכשירים. הזמינו יישום מודול BLE עם הבטחת איכות.
סריקת BLE ברקע אפשרית ב-iOS באמצעות שימור ושחזור מצב, ובאנדרואיד באמצעות מסנני סריקה. גם פיתוח Swift BLE וגם פיתוח Kotlin BLE דורשים טיפול זהיר בהרשאות.
ללימוד מעמיק של ה-API, עיינו בתיעוד הרשמי של CBCentralManager ושל BluetoothLeScanner.







