כשמתקשרים אל 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. מריצים את הקריאות באופן אסינכרוני על ידי הוספת שיטת קריאה חוזרת לאובייקט Task
, שמופעלת כשהפעולה מסתיימת:
Kotlin
// Using Kotlin function references task.addOnSuccessListener(::handleDataItem) task.addOnFailureListener(::handleDataItemError) task.addOnCompleteListener(::handleTaskComplete) ... fun handleDataItem(dataItem: DataItem) { ... } fun handleDataItemError(exception: Exception) { ... } fun handleTaskComplete(task: Task<DataItem>) { ... }
Java
// Using Java 8 Lambdas. task.addOnSuccessListener(dataItem -> handleDataItem(dataItem)); task.addOnFailureListener(exception -> handleDataItemError(exception)); task.addOnCompleteListener(task -> handleTaskComplete(task));
אפשרויות נוספות, כולל שרשור של ביצוע משימות שונות, מפורטות במאמר בנושא Task API.
שיחות סינכרוניות
אם הקוד שלכם פועל ב-thread נפרד של handler בשירות רקע, כמו ב-
WearableListenerService
, אין בעיה שהקריאות ייחסמו. במקרה כזה, אפשר להתקשר אל Tasks.await()
באובייקט Task
, שחוסם עד שהבקשה מסתיימת ומחזיר אובייקט Result
. הדוגמה הבאה ממחישה את זה.
הערה: חשוב לא להפעיל את הפונקציה הזו בשרשור הראשי.
Kotlin
try { Tasks.await(dataItemTask).apply { Log.d(TAG, "Data item set: $uri") } } catch (e: ExecutionException) { ... } catch (e: InterruptedException) { ... }
Java
try { DataItem item = Tasks.await(dataItemTask); Log.d(TAG, "Data item set: " + item.getUri()); } catch (ExecutionException | InterruptedException e) { ... }
האזנה לאירועים בשכבת הנתונים
שכבת הנתונים מסנכרנת ושולחת נתונים בין מכשירים ניידים ומכשירים לבישים, ולכן בדרך כלל צריך להאזין לאירועים חשובים כמו יצירה של פריטי נתונים וקבלת הודעות.
יש שתי אפשרויות להאזנה לאירועים בשכבת הנתונים:
- יוצרים שירות שמרחיב את
WearableListenerService
. - יוצרים פעילות או מחלקה שמטמיעות את הממשק
DataClient.OnDataChangedListener
.
בשתי האפשרויות האלה, אתם מבטלים את שיטות הקריאה החוזרת של אירועי הנתונים עבור האירועים שאתם רוצים לטפל בהם.
הערה: כשבוחרים הטמעה של listener, כדאי לקחת בחשבון את השימוש בסוללה של האפליקציה. רכיב WearableListenerService
רשום במניפסט של האפליקציה ויכול להפעיל את האפליקציה אם היא לא פועלת כבר. אם אתם צריכים להאזין לאירועים רק כשהאפליקציה כבר פועלת, וזה לרוב המצב באפליקציות אינטראקטיביות, אל תשתמשו ב-WearableListenerService
. במקום זאת, צריך לרשום מאזין לשידור חי.
לדוגמה, אפשר להשתמש בשיטה addListener
של המחלקה DataClient
. הפעולה הזו יכולה להפחית את העומס על המערכת ואת השימוש בסוללה.
שימוש ב-WearableListenerService
בדרך כלל יוצרים מופעים של
WearableListenerService
באפליקציות לנייד ולמכשיר הלביש. עם זאת, אם אתם לא מעוניינים באירועי נתונים באחת מהאפליקציות, אתם לא צריכים להטמיע את השירות באפליקציה הזו.
לדוגמה, יכולה להיות אפליקציה לנייד שמגדירה ומקבלת אובייקטים של פריטי נתונים, ואפליקציה ללבוש שמקשיבה לעדכונים האלה כדי לעדכן את ממשק המשתמש שלה. אפליקציית המכשיר הלביש אף פעם לא מעדכנת את פריטי הנתונים, ולכן אפליקציית המכשיר הנייד לא מאזינה לאירועי נתונים מאפליקציית המכשיר הלביש.
אלה חלק מהאירועים שאפשר להאזין להם באמצעות WearableListenerService
:
-
onDataChanged()
: בכל פעם שאובייקט של פריט נתונים נוצר, נמחק או משתנה, המערכת מפעילה את הקריאה החוזרת הזו בכל הצמתים המחוברים. -
onMessageReceived()
: הודעה שנשלחת מצומת מפעילה את הקריאה החוזרת הזו בצומת היעד. -
onCapabilityChanged()
: כשפונקציה שמופיעה בפרסום של מופע של האפליקציה שלכם הופכת לזמינה ברשת, האירוע הזה מפעיל את הקריאה החוזרת הזו. אם אתם מחפשים צומת קרוב, אתם יכולים להריץ שאילתה על השיטהisNearby()
של הצמתים שסופקו בקריאה החוזרת.
אפשר גם להאזין לאירועים מ-
ChannelClient.ChannelCallback
, כמו onChannelOpened()
.
כל האירועים הקודמים מופעלים בשרשור ברקע, ולא בשרשור הראשי.
כדי ליצור WearableListenerService
, פועלים לפי השלבים הבאים:
- יוצרים מחלקה שמרחיבה את
WearableListenerService
. - האזנה לאירועים שמעניינים אתכם, כמו
onDataChanged()
. - מצהירים על מסנן Intent במניפסט של Android כדי להודיע למערכת על
WearableListenerService
. ההצהרה הזו מאפשרת למערכת לקשור את השירות שלכם לפי הצורך.
בדוגמה הבאה אפשר לראות איך מטמיעים WearableListenerService
פשוט:
Kotlin
private const val TAG = "DataLayerSample" private const val START_ACTIVITY_PATH = "/start-activity" private const val DATA_ITEM_RECEIVED_PATH = "/data-item-received" 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) } } }
Java
public class DataLayerListenerService extends WearableListenerService { private static final String TAG = "DataLayerSample"; private static final String START_ACTIVITY_PATH = "/start-activity"; private static final String DATA_ITEM_RECEIVED_PATH = "/data-item-received"; @Override public void onDataChanged(DataEventBuffer dataEvents) { 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. for (DataEvent event : dataEvents) { Uri uri = event.getDataItem().getUri(); // Get the node ID from the host value of the URI. String nodeId = uri.getHost(); // Set the data of the message to be the bytes of the URI. byte[] payload = uri.toString().getBytes(); // Send the RPC. Wearable.getMessageClient(this).sendMessage( nodeId, DATA_ITEM_RECEIVED_PATH, payload); } } }
בקטע הבא מוסבר איך להשתמש במסנן כוונות עם מאזין כזה.
שימוש בפילטרים עם WearableListenerService
מסנן כוונות לדוגמה WearableListenerService
שמוצגת בקטע הקודם יכול להיראות כך:
<service android:name=".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
מחליפה את הפעולה BIND_LISTENER
שהומלצה קודם, כך שרק אירועים ספציפיים מעירים את האפליקציה או מפעילים אותה. השינוי הזה משפר את יעילות המערכת ומפחית את צריכת הסוללה ואת התקורה האחרת שמשויכת לאפליקציה. בדוגמה הזו, השעון מאזין לפריט הנתונים /start-activity
, והטלפון מאזין לתגובת ההודעה /data-item-received
.
הכללים הרגילים להתאמת מסננים ב-Android חלים. אפשר לציין כמה שירותים בכל מניפסט, כמה מסנני כוונות בכל שירות, כמה פעולות בכל מסנן וכמה קטעי נתונים בכל מסנן. מסננים יכולים להתאים למארח עם תבנית wildcard או למארח ספציפי. כדי להתאים למארח עם תו כללי לחיפוש, משתמשים ב-host="*"
. כדי להתאים
למארח ספציפי, מציינים host=<node_id>
.
אפשר גם להתאים נתיב מילולי או קידומת נתיב. כדי לעשות זאת, צריך לציין תו כללי לחיפוש או מארח ספציפי. אחרת, המערכת מתעלמת מהנתיב שציינתם.
מידע נוסף על סוגי המסננים שנתמכים ב-Wear OS זמין במאמרי העזרה של ה-API בנושא
WearableListenerService
.
מידע נוסף על מסנני נתונים וכללי התאמה זמין במסמכי העזרה בנושא ה-API של רכיב המניפסט <data>
.
כשמתאימים מסנני כוונות, חשוב לזכור שני כללים חשובים:
- אם לא מציינים סכימה למסנן הכוונות, המערכת מתעלמת מכל שאר מאפייני ה-URI.
- אם לא מציינים מארח במסנן, המערכת מתעלמת מכל מאפייני הנתיב.
שימוש במאזין בזמן אמת
אם האפליקציה שלכם מתעניינת רק באירועים של שכבת הנתונים כשהמשתמש נמצא באינטראקציה עם האפליקציה, יכול להיות שהיא לא צריכה שירות שפועל לאורך זמן כדי לטפל בכל שינוי בנתונים. במקרה כזה, אפשר להאזין לאירועים בפעילות על ידי הטמעה של אחד או יותר מהממשקים הבאים:
DataClient.OnDataChangedListener
MessageClient.OnMessageReceivedListener
CapabilityClient.OnCapabilityChangedListener
ChannelClient.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
, בהתאם לממשקים שהטמעתם. השיטות האלה נקראות בשרשור הראשי, או שאפשר לצייןLooper
בהתאמה אישית באמצעותWearableOptions
.
דוגמה להטמעה של DataClient.OnDataChangedListener
:
Kotlin
class MainActivity : Activity(), DataClient.OnDataChangedListener { public override fun onResume() { Wearable.getDataClient(this).addListener(this) } override fun 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) } } } }
Java
public class MainActivity extends Activity implements DataClient.OnDataChangedListener { @Override public void onResume() { Wearable.getDataClient(this).addListener(this); } @Override protected void onPause() { Wearable.getDataClient(this).removeListener(this); } @Override public void onDataChanged(DataEventBuffer dataEvents) { for (DataEvent event : dataEvents) { if (event.getType() == DataEvent.TYPE_DELETED) { Log.d(TAG, "DataItem deleted: " + event.getDataItem().getUri()); } else if (event.getType() == DataEvent.TYPE_CHANGED) { Log.d(TAG, "DataItem changed: " + event.getDataItem().getUri()); } } } }
זהירות:
לפני שמשתמשים ב-Wearable Data Layer API, צריך לבדוק שהוא זמין במכשיר. אחרת, תתרחש חריגה. משתמשים במחלקה GoogleApiAvailability
כפי שהיא מיושמת ב-Horologist.
שימוש בפילטרים עם מאזינים בזמן אמת
כמו שציינו קודם, בדיוק כמו שאפשר לציין מסנני Intent לאובייקטים מבוססי-מניפסט WearableListenerService
, אפשר להשתמש במסנני Intent כשרושמים מאזין פעיל דרך Wearable API. אותם כללים חלים גם על מאזינים לשידורים חיים שמבוססים על API וגם על מאזינים שמבוססים על מניפסט.
דפוס נפוץ הוא לרשום listener עם נתיב ספציפי או קידומת נתיב בשיטה onResume()
של פעילות, ואז להסיר את ה-listener בשיטה onPause()
של הפעילות.
הטמעה של מאזינים באופן הזה מאפשרת לאפליקציה לקבל אירועים בצורה סלקטיבית יותר, וכך לשפר את העיצוב והיעילות שלה.