טיפול באירועים של שכבות נתונים ב-Wear

כשמבצעים קריאה ל-Data Layer API, אפשר לקבל בסטטוס השיחה בסיום השיחה. אפשר גם להאזין לאירועי נתונים כתוצאה משינויים בנתונים שהאפליקציה שלך עורכת במקום כלשהו רשת Wear OS by Google.

דוגמה לעבודה יעילה עם ה-Data Layer API, אפשר לעיין האפליקציה Android DataLayer Sample.

המתנה לסטטוס של קריאות לשכבת הנתונים

קריאות ל-Data Layer API — כמו קריאה באמצעות putDataItem ה-method של מחלקה DataClient — לפעמים מוחזר אובייקט Task<ResultType>. מיד אחרי שהאובייקט Task נוצר, הפעולה בהמתנה בתור ברקע. אם לא תבצעו שום פעולה נוספת לאחר מכן, הפעולה מסתיים בשקט.

אבל בדרך כלל רצוי לעשות משהו את התוצאה אחרי סיום הפעולה, כך שהאובייקט Task מאפשר להמתין לסטטוס התוצאה באופן אסינכרוני או סינכרוני.

קריאות אסינכרוניות

אם הקוד פועל בשרשור הראשי של ממשק המשתמש, אל תבצעו הפעלות של חסימה אל ממשק 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));

Tasks 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) {
  ...
}

האזנה לאירועים של שכבת נתונים

מכיוון ששכבת הנתונים מסנכרנת ושולחת נתונים במכשירים במכשירים לבישים, בדרך כלל צריך להאזין לאירועים חשובים כמו פריטי נתונים שנוצרים והודעות שמתקבלות.

יש שתי אפשרויות להאזנה לאירועים של שכבת נתונים:

בשתי האפשרויות האלה אפשר לבטל את השיטות לקריאה חוזרת של אירועי נתונים עבור אירועים שברצונך לטפל בהם.

הערה: כשבוחרים באפשרות הזו, כדאי להביא בחשבון את השימוש בסוללה של האפליקציה הטמעת מכשיר Listener. WearableListenerService רשום במניפסט של האפליקציה ויכול להפעיל את האפליקציה אם עדיין לא ריצה. אם אתם צריכים להאזין לאירועים רק כשהאפליקציה כבר פועלת, וזה קורה לעיתים קרובות באפליקציות אינטראקטיביות, WearableListenerService במקום זאת, מומלץ לרשום מאזינים בשידור חי. לדוגמה, אפשר להשתמש בשיטה addListener של DataClient. בכיתה. הפעולה הזו יכולה להפחית את העומס על המערכת ולהפחית את השימוש בסוללה.

שימוש ב-WearableListenerService

בדרך כלל יוצרים מופעים של WearableListenerService גם בגאדג'ט הלביש וגם לאפליקציות להחזקה ביד. אבל, אם אתם לא מעוניינים באירועי נתונים באחד אין צורך להטמיע את השירות באפליקציה הזו.

לדוגמה, אפשר ליצור אפליקציה להחזקה ביד שמגדירה ומקבלת אובייקטים של פריטי נתונים ואפליקציה לבישה שמזהה את העדכונים האלה כדי לעדכן את ממשק המשתמש שלה. האפליקציה הלבישה אף פעם לא מעדכנת אף אחד מפריטי הנתונים, לכן האפליקציה להחזקה ביד להאזין לאירועי נתונים מהאפליקציה הלבישה.

חלק מהאירועים שאפשר להאזין להם בעזרת WearableListenerService הם:

  • onDataChanged(): בכל פעם שאובייקט של פריט נתונים נוצר, נמחק או משתנה, המערכת מופעלת הקריאה החוזרת (callback) הזו בכל הצמתים המחוברים.
  • onMessageReceived(): הודעה שנשלחה מטריגר של צומת הקריאה החוזרת (callback) הזו בצומת היעד.
  • onCapabilityChanged(): כשאפשרות שמופע של האפליקציה מפרסמת מתאפשרת ברשת, האירוע הזה יפעיל את הקריאה החוזרת (callback). אם ברצונך לחפש של צומת קרוב, אפשר לשלוח שאילתה שיטת isNearby() של הצמתים שצוינו בקריאה החוזרת (callback).

אפשר גם להאזין לאירועים מ- ChannelClient.ChannelCallback, כמו onChannelOpened().

כל האירועים הקודמים מבוצעים בשרשור ברקע, לא בשרשור הראשי.

כדי ליצור WearableListenerService, פועלים לפי השלבים הבאים:

  1. יצירת כיתה שמרחיבים את WearableListenerService.
  2. להאזין לאירועים שמעניינים אותך, כמו onDataChanged().
  3. יש להצהיר על מסנן 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);
        }
    }
}

בקטע הבא מוסבר איך להשתמש במסנן Intent עם המאזינים האלה.

שימוש במסננים עם WearableListenerService

מסנן Intent לדוגמה של 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. אפשר לציין כמה שירותים לכל מניפסט, מספר מסנני Intent לכל שירות, מספר פעולות לכל מסנן ומכמה שורות נתונים לכל מסנן. המסננים יכולים להתאים במארח תו כללי לחיפוש או במופעל ספציפית. כדי לבצע התאמה במארח עם תו כללי לחיפוש, יש להשתמש בפונקציה host="*". התאמה במארח ספציפי, מציינים את host=<node_id>.

אפשר גם להתאים ערך מילולי או קידומת של נתיב. כדי לעשות את זה, יש לציין תו כללי לחיפוש או מארח ספציפי. אחרת, המערכת תתעלם מהנתיב שציינתם.

למידע נוסף על סוגי המסננים שנתמכים ב-Wear OS, אפשר לעיין במאמר בנושא מאמרי העזרה של ה-API עבור WearableListenerService

מידע נוסף על מסנני נתונים וכללי התאמה זמין בהפניית ה-API מסמכים עבור <data> רכיב מניפסט.

כשמתאימים בין מסנני Intent, חשוב לזכור שני כללים חשובים:

  • אם לא צוינה סכמה למסנן Intent, המערכת מתעלמת כל שאר מאפייני ה-URI.
  • אם לא צוין מארח למסנן, המערכת תתעלם את כל מאפייני הנתיב.

שימוש בהאזנה בזמן אמת

אם לאפליקציה שלכם אכפת רק מאירועים בשכבת הנתונים כשהמשתמש יוצר אינטראקציה איתם את האפליקציה, ייתכן שהיא לא תצטרך שירות ארוך טווח כדי לטפל בכל שינוי בנתונים. לחשבון במקרה כזה, אפשר להאזין לאירועים בפעילות על ידי הטמעה של אירוע אחד או באחד מהממשקים הבאים:

כדי ליצור פעילות שמאזינים לאירועי נתונים:

  1. להטמיע את הממשקים הרצויים.
  2. בשיטה onCreate() או onResume(), מתקשרים Wearable.getDataClient(this).addListener(), MessageClient.addListener(), CapabilityClient.addListener(), או ChannelClient.registerChannelCallback() כדי לשלוח הודעה ל-Google Play השירותים שאתם מעוניינים להאזין להם עבור אירועים של שכבת נתונים.
  3. בתיקייה onStop() או onPause(), ביטול רישום של מאזינים ב-DataClient.removeListener(), MessageClient.removeListener(), CapabilityClient.removeListener(), או ChannelClient.unregisterChannelCallback().
  4. אם פעילות מסוימת מתעניינת רק באירועים עם קידומת של נתיב ספציפית, יכול להוסיף אוזן עם מסנן תחילית מתאים כדי לקבל רק נתונים שרלוונטיים למצב הנוכחי של האפליקציה.
  5. הטמעה של onDataChanged(), onMessageReceived(), onCapabilityChanged(), או methods מ-ChannelClient.ChannelCallback, בהתאם לממשקים שהטמעתם. השיטות האלה מופעלות של ה-thread הראשי, או שאפשר לציין 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());
            }
        }
    }
}

שימוש בפילטרים עם מאזינים בשידור חי

כפי שצוין קודם, בדיוק כמו שאפשר לציין מסנני Intent עבור WearableListenerService מבוסס מניפסט ניתן להשתמש במסנני Intent במהלך רישום של האזנה פעילה באמצעות גאדג'טים לבישים API. אותם כללים חלים גם על מאזינים בזמן אמת שמבוססים על API וגם על מאזינים שמבוססים על מניפסטים.

דפוס נפוץ הוא רישום מאזינים עם נתיב ספציפי או תחילית ספציפית של נתיב onResume() של פעילות ואז להסיר את המאזינים בפעילות onPause(). הטמעת מאזינים באופן הזה מאפשרת לאפליקציה באופן סלקטיבי יותר לקבל אירועים, ולשפר את העיצוב והיעילות שלו.