在 Wear 上處理資料層事件

Stay organized with collections Save and categorize content based on your preferences.

呼叫 Data Layer API 時,您可以在呼叫完成後收到呼叫的狀態。如果應用程式在 Wear OS by Google 網路中的任何位置發生資料變更,您也可以監聽資料事件。

請參閱下列相關資源:

等待資料層呼叫的狀態

呼叫 Data Layer API (例如使用 DataClient 類別的 putDataItem 方法呼叫) 有時會傳回 Task<ResultType> 物件。建立 Task 物件後,這項作業會在背景中排入佇列。如果操作完成後不再執行其他動作,作業最後將會無聲完成。不過,您通常會希望在作業完成後對結果執行某些操作,因此 Task 物件可讓您以同步或非同步的方式等待結果狀態。

非同步呼叫

如果程式碼在主要 UI 執行緒上執行,請勿對 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」瞭解其他可能性,包括將不同工作執行鏈結在一起的相關功能。

同步呼叫

如果程式碼在背景服務中的個別處理常式執行緒上執行 (也就是 WearableListenerService 中的情況),則可以進行封鎖呼叫。在這種情況下,您可以在 Task 物件上呼叫 Tasks.await(),在要求完成並傳回 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,如果應用程式尚未執行,則可啟動。如果只需要在應用程式執行期間監聽事件 (這通常是互動式應用程式的情況),請勿使用 WearableListenerService。例如,使用 DataClient 類別的 addListener 方法註冊事件監聽器。這麼做可以降低系統負載並降低電池用量。

使用 WearableListenerService

您通常在穿戴式和手持應用程式中,建立這項服務的執行個體。如果對其中一個應用程式的資料事件不感興趣,就不需要在特定應用程式中實作這項服務。

例如,您可以建立手持應用程式來設定及取得資料項目物件,以及穿戴式應用程式可以監聽這些更新以更新使用者介面。穿戴式裝置一律不會更新任何資料項目,因此手持應用程式不會監聽穿戴式應用程式的任何資料事件。

您可以使用 WearableListenerService 監聽部分的事件,如下所示:

  • onDataChanged():每當建立、刪除或變更資料項目物件時,系統會在所有連結的節點上觸發這個回呼。
  • onMessageReceived():從節點傳送的訊息會在目標節點上觸發這個回呼。
  • onCapabilityChanged():當應用程式的廣告執行個體在網路上變成可行的時候,則該事件就會觸發這個回呼。如要尋找附近的節點,可以查詢回呼中提供節點的 isNearby() 方法。

除了這份清單中的事件外,您也可以監聽 ChannelClient.ChannelCallback 的事件,例如 onChannelOpened()

上述所有事件都是在背景執行緒中執行,而不是在主執行緒中執行。

如要建立 WearableListenerService,請按照下列步驟操作:

  1. 建立可擴充 WearableListenerService 的類別。
  2. 監聽感興趣的事件,例如 onDataChanged()
  3. 在 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">
  <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 篩選器比對規則。您可以為每項資訊清單指定多項服務、為每個服務指定多個意圖篩選器、為每個篩選器指定多個動作,以及為每個篩選器指定多個資料站。可以透過萬用字元主機或特定主機進行比對的篩選器。如要比對萬用字元主機,請使用 host="*"。如要比對特定主機,請指定 host=<node_id>

你也可以比對常值路徑或路徑前置字串。如要比對路徑或路徑前置字串,您必須指定萬用字元或特定主機。若不這麼做,系統會忽略您指定的路徑。

如要進一步瞭解 Wear OS 支援的篩選器類型,請參閱「WearableListenerService」的 API 參考資料說明文件。

如要進一步瞭解資料篩選器和比對規則,請參閱 data 資訊清單元素的 API 參考文件。

比對意圖篩選器時,請務必記住以下兩個重要規則:

  • 如未為意圖篩選器指定配置,系統會忽略所有其他 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(),或從 ChannelClient.ChannelCallback 得到的方法。這些方法會在主執行緒上呼叫,或者您可以使用 WearableOptions 指定自訂 Looper

以下為實作 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());
            }
        }
    }
}

搭配即時監聽器使用篩選器

如本頁先前所述,如同可以指定以資訊清單 WearableListenerService 物件為基礎的意圖篩選器,可以在註冊即時監聽器時透過穿戴式 API 使用意圖篩選器。同樣的規則也適用於以 API 為基礎的即時監聽器資訊清單監聽器。

常見的做法是透過在活動的 onResume() 方法中註冊具有特定路徑或路徑前置字串的事件監聽器,並在活動的 onPause() 方法中移除事件監聽器。以這種方式實作事件監聽器,可讓應用程式選擇性接收事件,從而改善其設計和效率。