在 Wear 上處理資料層事件

透過集合功能整理內容 你可以依據偏好儲存及分類內容。

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

如需有效使用 Data Layer API 的範例,請參閱「Android 資料層範例」應用程式。

等待資料層呼叫的狀態

呼叫 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 例項。不過,如果您對其中一個應用程式的資料事件不感興趣,就不需要在該應用程式中實作該服務。

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

您可以使用 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 物件指定意圖篩選器一樣,您也可以在透過 Wearable API 註冊即時事件監聽器時使用意圖篩選器。以 API 為基礎的即時事件監聽器,以及以資訊清單為基礎的事件監聽器皆須遵守相同規則。

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