כששולחים קריאה ל-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 כדי לראות אפשרויות נוספות, כולל שרשור של ביצוע משימות שונות.
שיחות סינכרוניות
אם הקוד פועל ב-thread נפרד של handler בשירות רקע, כמו ב-WearableListenerService, צריך להשתמש ב-runBlocking כדי לבצע קריאה חוסמת ל-putDataItem.
הערה: אל תתקשרו באמצעות הפונקציה הזו כשאתם בשרשור הראשי.
private fun Context.sendDataSync(count: Int) { // Create a data item with the path and data to be sent val putDataReq: PutDataRequest = PutDataMapRequest.create("/count").run { dataMap.putInt("count_key", count) asPutDataRequest() } // Create a task to send the data to the data layer val task: Task<DataItem> = Wearable.getDataClient(this).putDataItem(putDataReq) try { Tasks.await(task).apply { // Add your logic here } } catch (e: ExecutionException) { // TODO: Handle exception } catch (e: InterruptedException) { // TODO: Handle exception Thread.currentThread().interrupt() } }
האזנה לאירועים בשכבת הנתונים
שכבת הנתונים מסנכרנת ושולחת נתונים בין המכשיר הנייד לבין המכשיר הלביש, ולכן בדרך כלל צריך להאזין לאירועים חשובים כמו יצירה של פריטי נתונים וקבלת הודעות.
יש שתי אפשרויות להאזנה לאירועים בשכבת הנתונים:
- יוצרים שירות שמרחיב את
WearableListenerService. - יוצרים פעילות או מחלקה שמטמיעות את הממשק
DataClient.OnDataChangedListener.
בשתי האפשרויות האלה, אתם מבטלים את שיטות הקריאה החוזרת (callback) של אירועי הנתונים עבור האירועים שאתם רוצים לטפל בהם.
הערה: כשבוחרים הטמעה של 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.
- אם לא מציינים מארח למסנן, המערכת מתעלמת מכל מאפייני הנתיב.
שימוש במאזין חי
אם האפליקציה שלכם מתעניינת רק באירועים של שכבת הנתונים כשהמשתמש נמצא באינטראקציה עם האפליקציה, יכול להיות שהיא לא צריכה שירות שפועל לאורך זמן כדי לטפל בכל שינוי בנתונים. במקרה כזה, אפשר להאזין לאירועים בפעילות באמצעות הטמעה של אחד או יותר מהממשקים הבאים:
DataClient.OnDataChangedListenerMessageClient.OnMessageReceivedListenerCapabilityClient.OnCapabilityChangedListenerChannelClient.ChannelCallback
כדי ליצור פעילות שמחכה לאירועי נתונים, פועלים לפי השלבים הבאים:
- מטמיעים את הממשקים הנדרשים.
- בשיטה
onCreate()או בשיטהonResume(), קוראים ל-Wearable.getDataClient(this).addListener(), ל-MessageClient.addListener(), ל-CapabilityClient.addListener()או ל-ChannelClient.registerChannelCallback()כדי להודיע ל-Google Play Services שהפעילות שלכם מתעניינת באירועים של שכבת הנתונים. - ב-
onStop()או ב-onPause(), מבטלים את הרישום של כל פונקציות ה-listener באמצעותDataClient.removeListener(),MessageClient.removeListener(),CapabilityClient.removeListener()אוChannelClient.unregisterChannelCallback(). - אם פעילות מסוימת צריכה לקבל רק אירועים עם קידומת נתיב ספציפית, צריך להוסיף מאזין עם מסנן קידומת כדי לקבל רק נתונים שרלוונטיים למצב הנוכחי של האפליקציה.
- מטמיעים את
onDataChanged(),onMessageReceived(),onCapabilityChanged()או שיטות מתוךChannelClient.ChannelCallback, בהתאם לממשקי המשתמש שהטמעתם. ה-methods האלה נקראות ב-thread הראשי, או שאפשר לצייןLooperבהתאמה אישית באמצעותWearableOptions.
דוגמה להטמעה של DataClient.OnDataChangedListener:
class MainActivity : Activity(), DataClient.OnDataChangedListener { public override fun onResume() { super.onResume() Wearable.getDataClient(this).addListener(this) } override fun onPause() { super.onPause() Wearable.getDataClient(this).removeListener(this) } override fun onDataChanged(dataEvents: DataEventBuffer) { dataEvents.forEach { event -> if (event.type == DataEvent.TYPE_DELETED) { Log.d(TAG, "DataItem deleted: " + event.dataItem.uri) } else if (event.type == DataEvent.TYPE_CHANGED) { Log.d(TAG, "DataItem changed: " + event.dataItem.uri) } } } }
זהירות: לפני שמשתמשים ב-Wearable Data Layer API, צריך לבדוק אם הוא זמין במכשיר. אחרת, תתרחש חריגה. משתמשים במחלקה
GoogleApiAvailability, כפי שהיא מוטמעת ב-Horologist.
שימוש בפילטרים עם מאזינים בזמן אמת
כמו שצוין קודם, בדיוק כמו שאפשר להגדיר מסנני Intent לאובייקטים של WearableListenerService שמבוססים על מניפסט, אפשר להשתמש במסנני Intent כשרושמים מאזין פעיל דרך Wearable API. אותם כללים חלים גם על מאזינים לשידורים חיים שמבוססים על API וגם על מאזינים שמבוססים על מניפסט.
דפוס נפוץ הוא לרשום listener עם נתיב ספציפי או קידומת נתיב בשיטה onResume() של פעילות, ואז להסיר את ה-listener בשיטה onPause() של הפעילות. הטמעה של פונקציות event listener באופן הזה מאפשרת לאפליקציה לקבל אירועים בצורה סלקטיבית יותר, וכך לשפר את העיצוב והיעילות שלה.