จัดการเหตุการณ์ชั้นข้อมูลใน Wear

เมื่อโทรไปยัง Data Layer API คุณจะได้รับสถานะของการโทรเมื่อเสร็จสมบูรณ์ นอกจากนี้ คุณยังฟังเหตุการณ์ข้อมูล ที่เกิดจากการเปลี่ยนแปลงข้อมูลที่แอปของคุณทำในเครือข่าย Wear OS by Google ได้ทุกที่

ดูตัวอย่างการทำงานกับ Data Layer API อย่างมีประสิทธิภาพได้ในแอป Android DataLayer Sample

รอสถานะของการเรียกใช้ Data Layer

การเรียกใช้ Data Layer API เช่น การเรียกใช้โดยใช้เมธอด putDataItem ของคลาส DataClient บางครั้งจะแสดงออบเจ็กต์ Task<ResultType> ทันทีที่สร้างออบเจ็กต์ Task ระบบจะจัดคิวการดำเนินการ ไว้เบื้องหลัง หากคุณไม่ดำเนินการใดๆ เพิ่มเติมหลังจากนี้ การดำเนินการจะเสร็จสมบูรณ์โดยไม่มีการแจ้งเตือน

อย่างไรก็ตาม โดยปกติแล้วคุณมักต้องการทำบางอย่างกับ ผลลัพธ์หลังจากที่การดำเนินการเสร็จสมบูรณ์ ดังนั้นออบเจ็กต์ Task จึงช่วยให้คุณ รอสถานะผลลัพธ์ได้แบบอะซิงโครนัสหรือซิงโครนัส

การเรียกแบบอะซิงโครนัส

หากโค้ดทำงานในเทรด UI หลัก อย่าทำการเรียกที่บล็อกไปยัง Data Layer API เรียกใช้การเรียกแบบไม่พร้อมกันโดยเพิ่มเมธอด Callback ลงในออบเจ็กต์ 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

การเรียกแบบซิงโครนัส

หากโค้ดของคุณทำงานในเทรดแฮนเดิลแยกต่างหากในบริการเบื้องหลัง เช่น ใน 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) {
  ...
}

รอรับเหตุการณ์ชั้นข้อมูล

เนื่องจากชั้นข้อมูลจะซิงค์และส่งข้อมูลในอุปกรณ์พกพาและอุปกรณ์ที่สวมใส่ได้ คุณจึงมักจะต้องรอฟังเหตุการณ์สำคัญ เช่น การสร้างรายการข้อมูลและการรับข้อความ

หากต้องการรอรับเหตุการณ์ชั้นข้อมูล คุณมี 2 ตัวเลือก ดังนี้

ทั้ง 2 ตัวเลือกนี้จะลบล้างวิธีการเรียกกลับของเหตุการณ์ข้อมูลสําหรับ เหตุการณ์ที่คุณสนใจจัดการ

หมายเหตุ: โปรดพิจารณาการใช้แบตเตอรี่ของแอปเมื่อเลือก การติดตั้งใช้งาน Listener WearableListenerService จะลงทะเบียนในไฟล์ Manifest ของแอปและสามารถเปิดแอปได้หากยังไม่ได้ เรียกใช้ หากคุณต้องการฟังเหตุการณ์เฉพาะเมื่อแอปทำงานอยู่แล้ว ซึ่งมักจะเป็นกรณีของแอปพลิเคชันแบบอินเทอร์แอกทีฟ ให้ไม่ต้องใช้ WearableListenerService แต่ให้ลงทะเบียนผู้ฟังที่ฟังสดแทน เช่น ใช้วิธี addListener ของคลาส DataClient ซึ่งจะช่วยลดภาระงานในระบบและลดการใช้งานแบตเตอรี่ได้

ใช้ WearableListenerService

โดยปกติแล้ว คุณจะสร้างอินสแตนซ์ของ WearableListenerService ทั้งในแอปสำหรับอุปกรณ์สวมใส่และแอปสำหรับอุปกรณ์ถือ อย่างไรก็ตาม หากไม่สนใจเหตุการณ์ข้อมูลในแอปใดแอปหนึ่ง คุณก็ไม่จำเป็นต้องติดตั้งใช้งานบริการในแอปนั้น

เช่น คุณอาจมีแอปแบบพกพาที่ตั้งค่าและรับออบเจ็กต์รายการข้อมูล และแอปบนอุปกรณ์ที่สวมใส่ได้ที่รอรับการอัปเดตเหล่านี้เพื่ออัปเดต UI แอปที่ใช้กับอุปกรณ์สวมใส่จะไม่เคยอัปเดตรายการข้อมูลใดๆ ดังนั้นแอปที่ใช้กับอุปกรณ์พกพาจึงไม่ ฟังเหตุการณ์ข้อมูลใดๆ จากแอปที่ใช้กับอุปกรณ์สวมใส่

เหตุการณ์บางอย่างที่คุณสามารถฟังได้โดยใช้ WearableListenerService มีดังนี้

  • onDataChanged(): เมื่อใดก็ตามที่มีการสร้าง ลบ หรือเปลี่ยนแปลงออบเจ็กต์รายการข้อมูล ระบบจะทริกเกอร์ การเรียกกลับนี้ในโหนดที่เชื่อมต่อทั้งหมด
  • onMessageReceived(): ข้อความที่ส่งจากโหนดจะทริกเกอร์ การเรียกกลับนี้ในโหนดเป้าหมาย
  • onCapabilityChanged(): เมื่อความสามารถที่อินสแตนซ์ของแอปโฆษณาพร้อมใช้งาน ในเครือข่าย เหตุการณ์ดังกล่าวจะทริกเกอร์การเรียกกลับนี้ หากกำลังมองหาโหนดที่อยู่ใกล้เคียง คุณสามารถค้นหาเมธอด isNearby() ของโหนดที่ระบุไว้ในการเรียกกลับ

นอกจากนี้ คุณยังฟังเหตุการณ์จาก ChannelClient.ChannelCallback เช่น onChannelOpened() ได้ด้วย

เหตุการณ์ก่อนหน้าทั้งหมดจะดำเนินการในเธรดเบื้องหลัง ไม่ใช่ในเธรดหลัก

หากต้องการสร้าง WearableListenerService ให้ทำตามขั้นตอนต่อไปนี้

  1. สร้างคลาสที่ขยาย WearableListenerService
  2. ฟังเหตุการณ์ที่คุณสนใจ เช่น onDataChanged()
  3. ประกาศตัวกรอง Intent ในไฟล์ Manifest ของ 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 กับ Listener นี้

ใช้ตัวกรองกับ 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 มาตรฐาน คุณระบุบริการหลายรายการ ต่อไฟล์ Manifest, ตัวกรอง Intent หลายรายการต่อบริการ, การดำเนินการหลายรายการต่อตัวกรอง และกลุ่มข้อมูลหลายรายการต่อตัวกรองได้ ตัวกรองสามารถจับคู่กับโฮสต์ไวลด์การ์ดหรือโฮสต์ที่เฉพาะเจาะจงได้ หากต้องการจับคู่ในโฮสต์ไวลด์การ์ด ให้ใช้ host="*" หากต้องการจับคู่ ในโฮสต์ที่เฉพาะเจาะจง ให้ระบุ host=<node_id>

นอกจากนี้ คุณยังจับคู่เส้นทางที่ตรงกันทุกประการหรือค่าต่อท้ายของเส้นทางได้ด้วย โดยคุณต้องระบุสัญลักษณ์แทนหรือโฮสต์ที่เฉพาะเจาะจง มิฉะนั้น ระบบจะไม่สนใจเส้นทางที่คุณระบุ

ดูข้อมูลเพิ่มเติมเกี่ยวกับประเภทตัวกรองที่ Wear OS รองรับได้ที่เอกสารประกอบการอ้างอิง API สำหรับ WearableListenerService

ดูข้อมูลเพิ่มเติมเกี่ยวกับตัวกรองข้อมูลและกฎการจับคู่ได้ที่เอกสารอ้างอิง API สำหรับองค์ประกอบ Manifest ของ <data>

เมื่อจับคู่ตัวกรองความตั้งใจ โปรดคำนึงถึงกฎสำคัญ 2 ข้อต่อไปนี้

  • หากไม่ได้ระบุสคีมาสำหรับตัวกรอง Intent ระบบจะละเว้นแอตทริบิวต์ URI อื่นๆ ทั้งหมด
  • หากไม่ได้ระบุโฮสต์สำหรับตัวกรอง ระบบจะไม่สนใจแอตทริบิวต์เส้นทางทั้งหมด

ใช้ผู้ฟังสด

หากแอปของคุณสนใจเฉพาะเหตุการณ์ชั้นข้อมูลเมื่อผู้ใช้โต้ตอบกับแอป คุณอาจไม่จำเป็นต้องมีบริการที่ทำงานเป็นเวลานานเพื่อจัดการการเปลี่ยนแปลงข้อมูลทุกครั้ง ในกรณีเช่นนี้ คุณสามารถรอฟังเหตุการณ์ในกิจกรรมได้โดยการใช้หนึ่งในอินเทอร์เฟซต่อไปนี้หรือมากกว่า

หากต้องการสร้างกิจกรรมที่รอรับเหตุการณ์ข้อมูล ให้ทำดังนี้

  1. ใช้การติดตั้งใช้งานอินเทอร์เฟซที่ต้องการ
  2. ในเมธอด onCreate() หรือ onResume() ให้เรียก Wearable.getDataClient(this).addListener(), MessageClient.addListener(), CapabilityClient.addListener() หรือ ChannelClient.registerChannelCallback() เพื่อแจ้งให้บริการของ Google Play ทราบว่ากิจกรรมของคุณสนใจรับฟังเหตุการณ์ใน Data Layer
  3. ใน onStop() หรือ onPause() ยกเลิกการลงทะเบียน Listener ด้วย DataClient.removeListener() MessageClient.removeListener() CapabilityClient.removeListener() หรือ ChannelClient.unregisterChannelCallback()
  4. หากกิจกรรมสนใจเฉพาะเหตุการณ์ที่มีคำนำหน้าเส้นทางที่เฉพาะเจาะจง คุณสามารถเพิ่ม Listener ที่มีตัวกรองคำนำหน้าที่เหมาะสมเพื่อรับเฉพาะข้อมูลที่เกี่ยวข้องกับสถานะแอปพลิเคชันปัจจุบัน
  5. ใช้เมธอด 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 ให้ตรวจสอบว่า API นี้พร้อมใช้งานใน อุปกรณ์หรือไม่ มิฉะนั้นจะเกิดข้อยกเว้น ใช้คลาส GoogleApiAvailability ตามที่ใช้ใน Horologist

ใช้ฟิลเตอร์กับผู้ฟังแบบเรียลไทม์

ดังที่ได้กล่าวไปแล้ว คุณสามารถใช้ตัวกรอง Intent เมื่อลงทะเบียนผู้ฟังแบบสดผ่าน Wearable API ได้เช่นเดียวกับการระบุตัวกรอง Intent สำหรับออบเจ็กต์ WearableListenerService ที่อิงตามไฟล์ Manifest กฎเดียวกันนี้จะมีผลกับทั้งผู้ฟังแบบสดที่ใช้ API และ ผู้ฟังที่ใช้ไฟล์ Manifest

รูปแบบที่ใช้กันทั่วไปคือการลงทะเบียน Listener ด้วยเส้นทางหรือคำนำหน้าเส้นทางที่เฉพาะเจาะจง ในเมธอด onResume() ของกิจกรรม แล้วนำ Listener ออกในเมธอด onPause() ของกิจกรรม การใช้เครื่องมือฟังในลักษณะนี้จะช่วยให้แอปรับเหตุการณ์ได้อย่างเลือกสรรมากขึ้น ซึ่งจะช่วยปรับปรุงการออกแบบและประสิทธิภาพของแอป