Menangani peristiwa Lapisan Data di perangkat Wear

Ketika melakukan panggilan ke Data Layer API, Anda bisa mendapatkan status panggilan tersebut ketika sudah selesai. Anda juga dapat memproses peristiwa data yang dihasilkan dari perubahan data yang dilakukan aplikasi Anda di mana pun di jaringan Wear OS by Google.

Untuk contoh cara bekerja secara efektif dengan Data Layer API, lihat aplikasi Contoh Android DataLayer.

Menunggu status panggilan Data Layer

Panggilan ke Data Layer API—misalnya panggilan yang menggunakan metode putDataItem dari class DataClient—terkadang menampilkan objek Task<ResultType>. Segera setelah objek Task dibuat, operasi akan dimasukkan ke antrean di latar belakang. Jika Anda tidak melakukan apa pun setelah ini, operasi akan diselesaikan tanpa pemberitahuan.

Namun, setelah operasi ini selesai, biasanya ada hal yang ingin Anda lakukan dengan hasilnya sehingga objek Task akan membiarkan Anda menunggu status hasilnya, baik secara asinkron maupun sinkron.

Panggilan asinkron

Jika kode Anda berjalan di UI thread utama, jangan buat panggilan pemblokiran ke Data Layer API. Jalankan panggilan secara asinkron dengan menambahkan metode callback ke objek Task, yang diaktifkan saat operasi selesai:

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));

Lihat Task API untuk kemungkinan lainnya, termasuk menggabungkan eksekusi tugas yang berbeda.

Panggilan tersinkron

Jika kode Anda berjalan pada thread pengendali yang berbeda di layanan latar belakang, seperti dalam WearableListenerService, tidak masalah jika panggilan memblokirnya. Dalam hal ini, Anda dapat memanggil Tasks.await() pada objek Task yang melakukan pemblokiran hingga permintaan selesai dan menampilkan objek Result. Hal ini ditunjukkan dalam contoh berikut.

Catatan: Pastikan Anda tidak memanggilnya saat berada di thread utama.

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

Memproses peristiwa Lapisan Data

Karena lapisan data menyinkronkan dan mengirimkan data ke perangkat genggam dan wearable, biasanya Anda perlu memproses peristiwa penting seperti item data yang dibuat dan pesan yang diterima.

Untuk memproses peristiwa lapisan data, Anda memiliki dua opsi:

Dengan kedua opsi ini, Anda akan menggantikan metode callback peristiwa data untuk peristiwa yang ingin ditangani.

Catatan: Pertimbangkan penggunaan baterai aplikasi saat memilih implementasi pemroses. WearableListenerService terdaftar dalam manifes aplikasi dan dapat meluncurkan aplikasi jika belum berjalan. Jika Anda hanya perlu memproses peristiwa ketika aplikasi sudah berjalan, yang biasanya terjadi dengan aplikasi interaktif, jangan gunakan WearableListenerService. Namun, daftarkan pemroses langsung. Misalnya, gunakan metode addListener dari class DataClient. Hal ini dapat mengurangi beban sistem dan menghemat penggunaan baterai.

Menggunakan WearableListenerService

Biasanya, Anda harus membuat instance WearableListenerService di aplikasi wearable maupun perangkat genggam. Namun, jika tidak tertarik dengan peristiwa data dalam salah satu aplikasi, Anda tidak perlu mengimplementasikan layanan dalam aplikasi tersebut.

Misalnya, Anda boleh memiliki aplikasi perangkat genggam yang menetapkan dan mendapatkan objek item data serta aplikasi wearable yang memproses update ini untuk mengupdate UI-nya. Aplikasi wearable tidak pernah mengupdate item data apa pun sehingga aplikasi perangkat genggam tidak memproses peristiwa data apa pun dari aplikasi wearable.

Beberapa peristiwa yang dapat Anda proses menggunakan WearableListenerService adalah sebagai berikut:

  • onDataChanged(): setiap kali objek item data dibuat, dihapus, atau diubah, sistem akan memicu callback ini pada semua node yang terhubung.
  • onMessageReceived(): pesan yang dikirim dari node akan memicu callback ini pada node target.
  • onCapabilityChanged(): ketika kemampuan yang disebutkan oleh instance aplikasi Anda tersedia di jaringan, peristiwa tersebut akan memicu callback ini. Jika sedang mencari node terdekat, Anda dapat meminta metode isNearby() dari node yang disediakan dalam callback.

Anda juga dapat memproses peristiwa dari ChannelClient.ChannelCallback, seperti onChannelOpened().

Semua peristiwa di atas dijalankan di thread latar belakang, bukan di thread utama.

Untuk membuat WearableListenerService, ikuti langkah-langkah berikut:

  1. Buat class yang memperluas WearableListenerService.
  2. Proses peristiwa yang menarik bagi Anda, seperti onDataChanged().
  3. Deklarasikan filter intent dalam manifes Android untuk memberi tahu sistem tentang WearableListenerService Anda. Deklarasi ini memungkinkan sistem mengikat layanan sesuai kebutuhan.

Contoh berikut ini menunjukkan cara mengimplementasikan WearableListenerService sederhana:

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);
        }
    }
}

Bagian berikutnya menjelaskan cara menggunakan filter intent dengan pemroses ini.

Menggunakan filter dengan WearableListenerService

Filter intent untuk contoh WearableListenerService yang ditampilkan di bagian sebelumnya mungkin terlihat seperti ini:

<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>

Dalam filter ini, tindakan DATA_CHANGED menggantikan tindakan BIND_LISTENER yang sebelumnya direkomendasikan sehingga hanya peristiwa tertentu yang akan mengaktifkan atau meluncurkan aplikasi Anda. Perubahan ini meningkatkan efisiensi sistem dan menghemat penggunaan baterai serta overhead lainnya terkait aplikasi Anda. Dalam contoh ini, smartwatch memproses item data /start-activity, dan ponsel memproses respons pesan /data-item-received.

Aturan pencocokan filter Android standar berlaku. Anda dapat menetapkan beberapa layanan per manifes, beberapa filter intent per layanan, beberapa tindakan per filter, dan beberapa stanza data per filter. Filter dapat mencocokkan host pengganti atau host tertentu. Untuk mencocokkan host pengganti, gunakan host="*". Untuk mencocokkan host tertentu, tetapkan host=<node_id>.

Anda juga dapat mencocokkan jalur literal atau awalan jalur. Untuk melakukannya, Anda harus menetapkan host pengganti atau host tertentu. Jika tidak, sistem akan mengabaikan jalur yang Anda tetapkan.

Untuk mengetahui informasi selengkapnya tentang jenis filter yang didukung Wear OS, lihat dokumentasi referensi API untuk WearableListenerService.

Untuk mengetahui informasi selengkapnya tentang aturan pencocokan dan filter data, lihat dokumentasi referensi API untuk elemen manifes <data>.

Saat mencocokkan filter intent, ingatlah dua aturan penting:

  • Jika tidak ada skema yang ditetapkan untuk filter intent, sistem akan mengabaikan semua atribut URI lainnya.
  • Jika tidak ada host yang ditetapkan untuk filter, sistem akan mengabaikan semua atribut jalur.

Menggunakan pemroses langsung

Jika aplikasi hanya berfokus pada peristiwa lapisan data ketika pengguna berinteraksi dengan aplikasi, layanan berdurasi panjang mungkin tidak diperlukan untuk menangani setiap perubahan data. Dalam hal ini, Anda dapat memproses peristiwa dalam aktivitas dengan mengimplementasikan satu atau beberapa antarmuka berikut:

Untuk membuat aktivitas yang memproses peristiwa data, lakukan langkah berikut:

  1. Implementasikan antarmuka yang diinginkan.
  2. Di metode onCreate() atau onResume(), panggil Wearable.getDataClient(this).addListener(), MessageClient.addListener(), CapabilityClient.addListener(), atau ChannelClient.registerChannelCallback() untuk memberi tahu layanan Google Play bahwa aktivitas Anda tertarik untuk memproses peristiwa lapisan data.
  3. Di onStop() atau onPause(), batalkan pendaftaran semua pemroses dengan DataClient.removeListener(), MessageClient.removeListener(), CapabilityClient.removeListener(), atau ChannelClient.unregisterChannelCallback().
  4. Jika suatu aktivitas hanya tertarik pada peristiwa yang memiliki awalan jalur tertentu, Anda dapat menambahkan pemroses dengan filter awalan yang sesuai agar hanya menerima data yang relevan dengan status aplikasi saat ini.
  5. Implementasikan onDataChanged(), onMessageReceived(), onCapabilityChanged(), atau metode dari ChannelClient.ChannelCallback, bergantung pada antarmuka yang Anda implementasikan. Metode ini dipanggil pada thread utama, atau Anda dapat menentukan Looper kustom menggunakan WearableOptions.

Berikut adalah contoh yang mengimplementasikan 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());
            }
        }
    }
}

Menggunakan filter dengan pemroses langsung

Seperti yang telah disebutkan, sama seperti Anda dapat menentukan filter intent untuk objek WearableListenerService berbasis manifes, Anda juga dapat menggunakan filter intent saat mendaftarkan pemroses langsung melalui Wearable API. Aturan yang sama berlaku untuk pemroses langsung berbasis API maupun pemroses berbasis manifes.

Pola yang cukup umum adalah mendaftarkan pemroses dengan jalur tertentu atau awalan jalur dalam metode onResume() aktivitas, lalu menghapus pemroses dalam metode onPause() aktivitas. Mengimplementasikan pemroses dengan cara ini memungkinkan aplikasi Anda menerima peristiwa secara lebih selektif sehingga meningkatkan desain dan efisiensinya.