כששולחים קריאה ל-Data Layer API, אפשר לקבל את הסטטוס של הקריאה כשהיא מסתיימת. אתם יכולים גם להאזין לאירועי נתונים שנובעים משינויים בנתונים שהאפליקציה שלכם מבצעת בכל מקום ברשת Wear OS by Google.
דוגמה לשימוש יעיל ב-Data Layer API מופיעה באפליקציה Android DataLayer Sample.
המתנה לסטטוס של קריאות לשכבת הנתונים
קריאות ל-Data Layer API – כמו קריאה באמצעות השיטה putDataItem של המחלקה DataClient – מחזירות לפעמים אובייקט Task<ResultType>. ברגע שאובייקט Task נוצר, הפעולה מתווספת לתור ברקע.
אם לא תבצעו פעולות נוספות אחרי זה, הפעולה תסתיים בסופו של דבר ללא התראה.
עם זאת, בדרך כלל רוצים לעשות משהו עם התוצאה אחרי שהפעולה מסתיימת, ולכן אובייקט Task מאפשר לכם להמתין לסטטוס התוצאה, באופן אסינכרוני או סינכרוני.
שיחות אסינכרוניות
אם הקוד שלכם פועל ב-thread הראשי של ממשק המשתמש, אל תבצעו קריאות חוסמות ל-Data Layer API, ותשתמשו ב-Coroutine כדי להפעיל את putDataItem:
private suspend fun Context.sendDataAsync(count: Int) { try { val putDataReq: PutDataRequest = PutDataMapRequest.create("/count").run { dataMap.putInt("count_key", count) asPutDataRequest() } val dataItem = Wearable.getDataClient(this).putDataItem(putDataReq).await() handleDataItem(dataItem) } catch (e: Exception) { handleDataItemError(e) } finally { handleTaskComplete() } } private fun handleDataItem(dataItem: DataItem) { } private fun handleDataItemError(exception: Exception) { } private fun handleTaskComplete() { }
אפשר לעיין בTask API כדי לראות אפשרויות נוספות, כולל שרשור של ביצוע משימות שונות.
שיחות סינכרוניות
אם הקוד פועל בשרשור נפרד של handler בשירות רקע, כמו ב-WearableListenerService, צריך להשתמש ב-runBlocking כדי לבצע קריאה חוסמת ל-putDataItem.
הערה: אל תתקשרו למספר הזה כשאתם בשרשור הראשי.
private fun Context.sendDataSync(count: Int) = runBlocking { val putDataReq = PutDataMapRequest.create("/count").run { dataMap.putInt("count_key", count) asPutDataRequest() } try { val result = Wearable.getDataClient(this@sendDataSync) .putDataItem(putDataReq) .await() // Logic for success } catch (e: Exception) { // Handle failure } }
האזנה לאירועים בשכבת הנתונים
שכבת הנתונים מסנכרנת ושולחת נתונים בין המכשיר הנייד והמכשיר הלביש, ולכן בדרך כלל צריך להאזין לאירועים חשובים כמו יצירה של פריטי נתונים וקבלת הודעות.
יש שתי אפשרויות להאזנה לאירועים בשכבת הנתונים:
- יוצרים שירות שמרחיב את
WearableListenerService. - יוצרים פעילות או מחלקה שמטמיעות את הממשק
DataClient.OnDataChangedListener.
בשתי האפשרויות האלה, אתם מבטלים את שיטות הקריאה החוזרת של אירועי הנתונים עבור האירועים שאתם רוצים לטפל בהם.
הערה: כשבוחרים הטמעה של listener, חשוב לקחת בחשבון את השימוש בסוללה של האפליקציה. WearableListenerService רשום במניפסט של האפליקציה, ויכול להפעיל את האפליקציה אם היא לא פועלת כבר. אם אתם צריכים להאזין לאירועים רק כשהאפליקציה כבר פועלת, כמו במקרים רבים של אפליקציות אינטראקטיביות, אל תשתמשו ב-WearableListenerService. במקום זאת, צריך לרשום מעבד אירוע של שידור חי. לדוגמה, אפשר להשתמש בשיטה addListener של המחלקה DataClient. הפעולה הזו יכולה להפחית את העומס על המערכת ואת השימוש בסוללה.
שימוש ב-WearableListenerService
בדרך כלל יוצרים מופעים של WearableListenerService באפליקציות לטלפון ולאפליקציות למכשירים לבישים. עם זאת, אם אתם לא מעוניינים באירועי נתונים באחת מהאפליקציות, אתם לא צריכים להטמיע את השירות באפליקציה הזו.
לדוגמה, יכולה להיות אפליקציה לנייד שמגדירה ומקבלת אובייקטים של פריטי נתונים, ואפליקציה למכשיר לביש שמקשיבה לעדכונים האלה כדי לעדכן את ממשק המשתמש שלה. אפליקציית המכשיר הלביש אף פעם לא מעדכנת את פריטי הנתונים, ולכן אפליקציית המכשיר הנייד לא מאזינה לאירועי נתונים מאפליקציית המכשיר הלביש.
אלה חלק מהאירועים שאפשר להאזין להם באמצעות WearableListenerService:
-
onDataChanged(): בכל פעם שאובייקט של פריט נתונים נוצר, נמחק או משתנה, המערכת מפעילה את הקריאה החוזרת הזו בכל הצמתים המחוברים. -
onMessageReceived(): הודעה שנשלחת מצומת מפעילה את הקריאה החוזרת הזו בצומת היעד. -
onCapabilityChanged(): כשממשק API שמופיע בפרסום של מופע של האפליקציה שלכם הופך לזמין ברשת, האירוע הזה מפעיל את הקריאה החוזרת הזו. אם אתם מחפשים צומת בקרבת מקום, אתם יכולים לשלוח שאילתה לשיטהisNearby()של הצמתים שסופקו בקריאה החוזרת.
אפשר גם להאזין לאירועים מ-ChannelClient.ChannelCallback, כמו onChannelOpened().
כל האירועים הקודמים מופעלים בשרשור ברקע, ולא בשרשור הראשי.
כדי ליצור WearableListenerService, פועלים לפי השלבים הבאים:
- יוצרים מחלקה שמרחיבה את
WearableListenerService. - האזנה לאירועים שמעניינים אתכם, כמו
onDataChanged(). - מצהירים על מסנן Intent במניפסט של Android כדי להודיע למערכת על
WearableListenerService. ההצהרה הזו מאפשרת למערכת לקשור את השירות שלכם לפי הצורך.
בדוגמה הבאה אפשר לראות איך מטמיעים WearableListenerService:
class DataLayerListenerService : WearableListenerService() { override fun onDataChanged(dataEvents: DataEventBuffer) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "onDataChanged: $dataEvents") } // Loop through the events and send a message // to the node that created the data item. dataEvents .map { it.dataItem.uri } .forEach { uri -> // Get the node ID from the host value of the URI. val nodeId: String = uri.host!! // Set the data of the message to be the bytes of the URI. val payload: ByteArray = uri.toString().toByteArray() // Send the RPC. Wearable.getMessageClient(this) .sendMessage( nodeId, DATA_ITEM_RECEIVED_PATH, payload ) } } }
בקטע הבא מוסבר איך להשתמש במסנן כוונות עם מאזין כזה.
שימוש בפילטרים עם WearableListenerService
מסנן כוונות לדוגמה WearableListenerService שמוצגת בקטע הקודם יכול להיראות כך:
<service android:name=".snippets.datalayer.DataLayerListenerService" android:exported="true" tools:ignore="ExportedService" > <intent-filter> <action android:name="com.google.android.gms.wearable.DATA_CHANGED" /> <data android:scheme="wear" android:host="*" android:path="/start-activity" /> </intent-filter> </service>
מסנן הפעולות DATA_CHANGED מציין למערכת שהאפליקציה שלכם מתעניינת באירועים של שכבת הנתונים.
בדוגמה הזו, השעון מאזין לפריט הנתונים /start-activity, והטלפון מאזין לתגובת ההודעה /data-item-received (DATA_ITEM_RECEIVED_PATH).
הכללים הרגילים להתאמת מסננים ב-Android חלים. אפשר לציין כמה שירותים בכל מניפסט, כמה מסנני כוונות בכל שירות, כמה פעולות בכל מסנן וכמה קטעי נתונים בכל מסנן. המסננים יכולים להתאים למארח עם תווים כלליים או למארח ספציפי. כדי להתאים למארח עם תו כללי לחיפוש, משתמשים ב-host="*". כדי להתאים למארח ספציפי, מציינים host=<node_id>.
אפשר גם להתאים נתיב מילולי או קידומת נתיב. כדי לעשות את זה, צריך לציין תו כללי לחיפוש או מארח ספציפי. אחרת, המערכת מתעלמת מהנתיב שציינתם.
מידע נוסף על סוגי המסננים שנתמכים ב-Wear OS זמין במאמרי העזרה של ה-API בנושא WearableListenerService.
מידע נוסף על מסנני נתונים וכללי התאמה זמין במסמכי העזרה בנושא API של רכיב המניפסט <data>.
כשמתאימים מסנני כוונות, חשוב לזכור שני כללים:
- אם לא מציינים סכימה למסנן הכוונות, המערכת מתעלמת מכל שאר מאפייני ה-URI.
- אם לא מציינים מארח למסנן, המערכת מתעלמת מכל מאפייני הנתיב.
שימוש במאזין חי
אם האפליקציה שלכם מתעניינת רק באירועים של שכבת הנתונים כשהמשתמש נמצא באינטראקציה עם האפליקציה, יכול להיות שהיא לא צריכה שירות שפועל לאורך זמן כדי לטפל בכל שינוי בנתונים. במקרה כזה, אפשר להאזין לאירועים בפעילות.
כדי להמליץ על גישה נקייה ובטוחה יותר, כדאי להשתמש ב-Lifecycle Observer. באמצעות Lifecycle Observer, מעבירים את לוגיקת הרישום מ-onResume() של Activity אל מחלקה נפרדת שאפשר לעשות בה שימוש חוזר ומטמיעה את DefaultLifecycleObserver.
הגישה הזו עוזרת לשמור על פעילות יעילה ומונעת באגים נפוצים, כמו שכחה לבטל את הרישום של ה-listener.
1. יצירת מאזין עם מודעות למחזור החיים
המחלקות האלה עוטפות את DataClient.OnDataChangedListener ומנהלות באופן אוטומטי את המינוי שלהן על סמך מחזור החיים של הפעילות.
class WearDataLayerObserver( private val dataClient: DataClient, private val onDataReceived: (DataEventBuffer) -> Unit ) : DefaultLifecycleObserver, DataClient.OnDataChangedListener { // Implementation of the DataClient listener override fun onDataChanged(dataEvents: DataEventBuffer) { onDataReceived(dataEvents) } // Automatically register when the Activity starts override fun onResume(owner: LifecycleOwner) { dataClient.addListener(this) } // Automatically unregister when the Activity pauses override fun onPause(owner: LifecycleOwner) { dataClient.removeListener(this) } }
2. השימוש בפעילות שלכם
עכשיו, הפעילות לא צריכה לבטל את onResume() או את onPause() עבור Wear API. מוסיפים את המשקיף פעם אחת ב-onCreate().
class DataLayerLifecycleActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val dataClient = Wearable.getDataClient(this) // Create the observer and link it to the activity's lifecycle val wearObserver = WearDataLayerObserver(dataClient) { dataEvents -> handleDataEvents(dataEvents) } lifecycle.addObserver(wearObserver) } private fun handleDataEvents(dataEvents: DataEventBuffer) { // ... filter and process events ... } }
למה זה עדיף:
- ניקוי הפעילות: הסרת קוד סטנדרטי משיטות מחזור החיים של הפעילות.
- בטיחות: השיטה
DefaultLifecycleObserverעוזרת לוודא שהמאזין מוסר גם אם הפעילות נהרסת באופן בלתי צפוי, וכך מונעת דליפות זיכרון. - אפשרות שימוש חוזר: אפשר לחבר את
WearDataLayerObserverלכל Activity או Fragment בלי לשכתב את לוגיקת הרישום. - הפרדה: הלוגיקה של מתי להאזין מופרדת מהלוגיקה של מה לעשות עם הנתונים.
שימוש בפילטרים עם מאזינים בזמן אמת
כמו שציינו קודם, בדיוק כמו שאפשר להגדיר מסנני Intent לאובייקטים מבוססי-מניפסט WearableListenerService, אפשר להשתמש במסנני Intent כשרושמים מאזין פעיל דרך Wearable API. אותם כללים חלים גם על מאזינים לשידורים חיים שמבוססים על API וגם על מאזינים שמבוססים על מניפסט.
דפוס נפוץ הוא רישום של מאזין עם נתיב ספציפי או קידומת נתיב
באמצעות LifecycleObserver. הטמעה של מאזינים באופן הזה מאפשרת לאפליקציה לקבל אירועים בצורה סלקטיבית יותר, וכך לשפר את העיצוב והיעילות שלה.