Xử lý các sự kiện Lớp dữ liệu trên Wear

Khi thực hiện lệnh gọi đến Data Layer API (API Lớp dữ liệu), bạn có thể nhận được trạng thái của lệnh gọi khi hoàn tất. Bạn cũng có thể theo dõi các sự kiện dữ liệu do những thay đổi về dữ liệu mà ứng dụng của bạn thực hiện ở bất cứ đâu trên mạng Wear OS by Google.

Để biết ví dụ về cách làm việc hiệu quả với Data Layer API (API Lớp dữ liệu), hãy xem ứng dụng Mẫu DataLayer của Android.

Chờ trạng thái của lệnh gọi Lớp dữ liệu

Các lệnh gọi đến Data Layer API (API Lớp dữ liệu), chẳng hạn như lệnh gọi sử dụng phương thức putDataItem của lớp DataClient, đôi khi trả về đối tượng Task<ResultType>. Ngay sau khi đối tượng Task được tạo, thao tác này sẽ được đưa vào hàng đợi trong chế độ nền. Nếu bạn không làm gì thêm sau đó thì thao tác này cuối cùng sẽ tự hoàn tất.

Tuy nhiên, thông thường, bạn sẽ muốn làm gì đó với kết quả sau khi thao tác hoàn tất. Vì vậy, đối tượng Task cho phép bạn đợi trạng thái kết quả một cách không đồng bộ hoặc đồng bộ.

Lệnh gọi không đồng bộ

Nếu mã đang chạy trên luồng giao diện người dùng chính, thì bạn đừng gửi lệnh gọi chặn đến Data Layer API (API Lớp dữ liệu). Hãy chạy các lệnh gọi không đồng bộ bằng cách thêm phương pháp gọi lại vào đối tượng Task. Phương pháp này sẽ kích hoạt khi thao tác hoàn tất:

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

Hãy xem Task API (API Nhiệm vụ) để biết các khả năng khác, bao gồm cả việc liên kết hoạt động thực thi nhiều nhiệm vụ.

Lệnh gọi đồng bộ

Nếu mã của bạn đang chạy trên một luồng riêng biệt của trình xử lý trong dịch vụ nền, chẳng hạn như trong WearableListenerService, thì bạn có thể chặn các lệnh gọi. Trong trường hợp này, bạn có thể gọi Tasks.await() trên đối tượng Task để chặn cho đến khi yêu cầu hoàn tất và trả về đối tượng Result. Lệnh này được minh hoạ trong ví dụ sau:

Lưu ý: Nhớ đừng gọi lệnh này khi đang ở trên luồng chính.

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

Theo dõi các sự kiện Lớp dữ liệu

Lớp dữ liệu sẽ đồng bộ hoá và gửi dữ liệu giữa các thiết bị cầm tay và thiết bị đeo. Vậy nên, thông thường, bạn cần theo dõi các sự kiện quan trọng như mục dữ liệu đang tạo và thông báo đang nhận.

Để theo dõi các sự kiện lớp dữ liệu, bạn có 2 lựa chọn:

Với cả hai lựa chọn này, bạn sẽ ghi đè các phương pháp gọi lại sự kiện dữ liệu cho các sự kiện mà bạn muốn xử lý.

Lưu ý: Hãy cân nhắc mức sử dụng pin của ứng dụng khi chọn phương thức triển khai cho trình nghe. WearableListenerService được đăng ký trong tệp kê khai của ứng dụng và có thể chạy ứng dụng nếu ứng dụng chưa chạy. Nếu bạn chỉ cần theo dõi các sự kiện khi ứng dụng đang chạy (thường là với các ứng dụng tương tác), thì bạn không nên sử dụng WearableListenerService. Thay vào đó, hãy đăng ký trình nghe trực tiếp. Ví dụ: sử dụng phương thức addListener của lớp DataClient. Việc này có thể giảm bớt tải trên hệ thống và giảm mức sử dụng pin.

Sử dụng WearableListenerService

Bạn thường tạo các thực thể của WearableListenerService trong cả ứng dụng cho thiết bị đeo và thiết bị cầm tay. Tuy nhiên, nếu không quan tâm đến các sự kiện dữ liệu ở một trong những ứng dụng nêu trên, thì bạn không cần triển khai dịch vụ trong ứng dụng đó.

Ví dụ: bạn có thể dùng một ứng dụng cho thiết bị cầm tay để đặt và lấy các đối tượng mục dữ liệu, một ứng dụng cho thiết bị đeo giúp theo dõi các bản cập nhật này để cập nhật giao diện người dùng. Ứng dụng cho thiết bị đeo tuyệt đối không cập nhật mục dữ liệu. Vì vậy, ứng dụng cho thiết bị cầm tay sẽ không theo dõi bất kỳ sự kiện dữ liệu nào từ ứng dụng cho thiết bị đeo.

Sau đây là một số sự kiện mà bạn có thể theo dõi bằng cách sử dụng WearableListenerService:

  • onDataChanged(): mỗi khi một đối tượng mục dữ liệu được tạo, bị xoá hoặc thay đổi, hệ thống sẽ kích hoạt lệnh gọi lại này trên tất cả các nút được kết nối.
  • onMessageReceived(): một thông báo gửi từ nút sẽ kích hoạt lệnh gọi lại này trên nút đích.
  • onCapabilityChanged(): khi một thực thể của ứng dụng cho biết một chức năng có sẵn trên mạng, thì sự kiện đó sẽ kích hoạt lệnh gọi lại này. Nếu đang tìm một nút gần đó, bạn có thể truy vấn phương thức isNearby() của các nút được cung cấp trong lệnh gọi lại.

Bạn cũng có thể theo dõi các sự kiện từ ChannelClient.ChannelCallback, chẳng hạn như onChannelOpened().

Mọi sự kiện trước đó được thực thi trong một luồng ở chế độ nền chứ không phải trên luồng chính.

Để tạo WearableListenerService, hãy làm theo các bước sau:

  1. Tạo một lớp mở rộng WearableListenerService.
  2. Theo dõi những sự kiện mà bạn quan tâm, chẳng hạn như onDataChanged().
  3. Khai báo bộ lọc ý định trong tệp kê khai Android để thông báo cho hệ thống về WearableListenerService. Thông tin khai báo này giúp hệ thống liên kết dịch vụ của bạn nếu cần.

Ví dụ sau đây trình bày cách triển khai một WearableListenerService đơn giản:

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

Phần sau đây giải thích cách sử dụng bộ lọc ý định bằng trình nghe này.

Sử dụng bộ lọc bằng WearableListenerService

Bộ lọc ý định cho ví dụ về WearableListenerService được trình bày trong phần trước có thể có dạng như sau:

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

Trong bộ lọc này, hành động DATA_CHANGED sẽ thay thế hành động BIND_LISTENER được đề xuất trước đó để chỉ những sự kiện cụ thể mới có thể đánh thức hoặc chạy ứng dụng của bạn. Thay đổi này cải thiện hiệu suất của hệ thống và giảm mức tiêu thụ pin cũng như mức hao tổn khác liên quan đến ứng dụng. Trong ví dụ này, đồng hồ sẽ theo dõi mục dữ liệu /start-activity, còn điện thoại sẽ theo dõi phản hồi tin nhắn /data-item-received.

Áp dụng quy tắc đối sánh bộ lọc tiêu chuẩn của Android Bạn có thể chỉ định nhiều dịch vụ cho mỗi tệp kê khai, nhiều bộ lọc ý định cho mỗi dịch vụ, nhiều hành động cho mỗi bộ lọc và nhiều khổ dữ liệu cho mỗi bộ lọc. Các bộ lọc có thể so khớp trên một máy chủ có chứng nhận ký tự đại diện hoặc trên một máy chủ cụ thể. Để so khớp trên máy chủ có chứng nhận ký tự đại diện, hãy sử dụng host="*". Để so khớp trên một máy chủ cụ thể, hãy chỉ định host=<node_id>.

Bạn cũng có thể so khớp một đường dẫn giá trị cố định hoặc tiền tố đường dẫn. Để làm việc này, bạn phải chỉ định một ký tự đại diện hoặc bộ lưu trữ cụ thể. Nếu không, hệ thống sẽ bỏ qua đường dẫn mà bạn chỉ định.

Để biết thêm thông tin về các loại bộ lọc mà Wear OS hỗ trợ, hãy xem tài liệu tham khảo API cho WearableListenerService.

Để biết thêm thông tin về bộ lọc dữ liệu và các quy tắc so khớp, hãy xem tài liệu tham khảo API cho phần tử tệp kê khai <data>.

Khi so khớp các bộ lọc ý định, hãy nhớ 2 quy tắc quan trọng:

  • Nếu không có lược đồ nào được chỉ định cho bộ lọc ý định, hệ thống sẽ bỏ qua mọi thuộc tính URI khác.
  • Nếu không có bộ lưu trữ nào được chỉ định cho bộ lọc, hệ thống sẽ bỏ qua mọi thuộc tính đường dẫn.

Sử dụng trình nghe trực tiếp

Nếu ứng dụng chỉ quan tâm đến các sự kiện lớp dữ liệu khi người dùng đang tương tác với ứng dụng, thì có thể ứng dụng không cần dịch vụ chạy trong thời gian dài để xử lý mọi thay đổi về dữ liệu. Trong trường hợp như vậy, bạn có thể theo dõi các sự kiện trong một hoạt động bằng cách triển khai một hoặc nhiều giao diện sau:

Để tạo một hoạt động giúp theo dõi các sự kiện dữ liệu, hãy làm như sau:

  1. Triển khai các giao diện mà bạn muốn.
  2. Trong phương thức onCreate() hoặc onResume(), hãy gọi Wearable.getDataClient(this).addListener(), MessageClient.addListener(), CapabilityClient.addListener() hoặc ChannelClient.registerChannelCallback() để thông báo cho các dịch vụ Google Play về việc hoạt động của bạn muốn theo dõi các sự kiện lớp dữ liệu.
  3. Trong onStop() hoặc onPause(), hãy huỷ đăng ký bất kỳ trình nghe nào bằng DataClient.removeListener(), MessageClient.removeListener(), CapabilityClient.removeListener() hoặc ChannelClient.unregisterChannelCallback().
  4. Nếu một hoạt động chỉ quan tâm đến các sự kiện có tiền tố đường dẫn cụ thể, thì bạn có thể thêm trình nghe có bộ lọc tiền tố phù hợp để chỉ nhận dữ liệu liên quan đến trạng thái ứng dụng hiện tại.
  5. Triển khai onDataChanged(), onMessageReceived(), onCapabilityChanged() hoặc phương thức từ ChannelClient.ChannelCallback tuỳ thuộc vào giao diện mà bạn đã triển khai. Các phương thức này được gọi trên luồng chính hoặc bạn có thể chỉ định Looper tuỳ chỉnh bằng cách sử dụng WearableOptions.

Dưới đây là ví dụ triển khai 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());
            }
        }
    }
}

Sử dụng các bộ lọc với trình nghe trực tiếp

Như đã đề cập trước đó, giống như việc có thể chỉ định bộ lọc ý định cho các đối tượng WearableListenerService dựa trên tệp kê khai, bạn có thể sử dụng bộ lọc ý định khi đăng ký trình nghe trực tiếp thông qua API cho thiết bị đeo. Các quy tắc tương tự áp dụng cho cả trình nghe trực tiếp dựa trên API và trình nghe dựa trên tệp kê khai.

Một mẫu phổ biến là đăng ký trình nghe bằng một đường dẫn hoặc tiền tố đường dẫn cụ thể trong phương thức onResume() của hoạt động, sau đó xoá trình nghe trong phương thức onPause() của hoạt động. Khi triển khai trình nghe theo cách này, ứng dụng của bạn có thể nhận các sự kiện một cách chọn lọc hơn, góp phần cải thiện thiết kế và tính hiệu quả của ứng dụng.