從 11.8.0 版的 Google Play 服務開始,Wear OS 應用程式應進行遷移,不再使用 GoogleApiClient
類別,而是改用基於
GoogleApi
類別的用戶端物件。
使用
GoogleApi
可以減少設定非同步作業的複雜度。舉例來說,您可以取得 Task
而非
PendingResult
物件,如 Tasks API 簡介所述。
本頁面收錄了:
- 替換元件表格
- 將現有應用程式更新為使用 Tasks API 的範例
注意:這項更新不適用於中國版 Wear OS 應用程式;這類應用程式通常採用 10.2.0 版的 Google Play 服務。
注意:目前只有和 Android 手機配對的 Android 手機和 Wear OS 手錶才能使用此 API。Wear OS 手錶與 iOS 手機配對時,在有網際網路連線的情況下,應用程式可以查詢其他雲端式 API。
替換已淘汰的元件
如果您使用的類別可以擴充
GoogleApi
類別,例如
DataClient
和
MessageClient
,那麼 Google Play 服務 SDK 會為您管理 Google Play 服務的連線。
使用以下替換類別的應用程式不需要建立及管理 GoogleApiClient
物件。另請參閱「存取 Google API」以及 Wearable 類別的參考資料頁面。
以下表格會列出已淘汰的元件及替換元件:
已淘汰的元件 | 替換元件 |
CapabilityApi
|
CapabilityClient
|
Channel
|
ChannelClient.Channel
|
ChannelApi
|
ChannelClient
|
DataApi
|
DataClient
|
MessageApi
|
MessageClient
|
NodeApi
|
NodeClient
|
此外,也請注意下列事項:
- 在頻道變更通知中,
ChannelClient.ChannelCallback
會取代Channel.ChannelListener
- 設定事件監聽器回呼的執行緒時,
WearableOptions.Builder
的setLooper
方法會取代GoogleApiClient.Builder.setHandler
Wear 應用程式遷移範例
以下程式碼片段可做為遷移範例,顯示採用 Data Layer API 的 Wear Data Layer 示例針對 Google Play 服務 11.8.0 版做了哪些更新。如果您的應用程式含有手機模組,更新方式可能會和 Wear 模組的類似。
更新 Google Play 服務的依附元件
由於應用程式可能已經依附較舊的 Google Play 服務版本,請針對 Wear 模組的 build.gradle
檔案更新以下依附元件:
dependencies { ... compile 'com.google.android.gms:play-services-wearable:11.8.0' }
更新應用程式的匯入陳述式
請匯入必要類別,包括 Tasks API 內的類別。
舉例來說,Wear Data Layer 示例之前在 MainActivity.java
檔案中含有以下匯入陳述式。您應移除這段 import
陳述式:
Kotlin
... import com.google.android.gms.common.api.GoogleApiClient ...
Java
... import com.google.android.gms.common.api.GoogleApiClient; ...
舉例來說,在 Wear Data Layer 示例中,上文所示的 import
陳述式已取代為以下內容 (第二段用來處理工作例外狀況):
Kotlin
... import com.google.android.gms.tasks.Tasks import java.util.concurrent.ExecutionException ...
Java
... import com.google.android.gms.tasks.Tasks; import java.util.concurrent.ExecutionException; ...
實作新用戶端介面
請移除所有 GoogleApiClient
類別和相關介面 (ConnectionCallbacks
、OnConnectionFailedListener
等) 的用例,然後將其他 Listener 實作替換為新版本。實際需要覆寫的方法通常都會繼承之前的名稱,所以主要變更內容會像下方範例這樣。
舉例來說,Wear Data Layer 示例的主要活動 (如 GitHub 的差異比較所示) 之前是實作 CapabilityApi.CapabilityListener
介面。但現在,主要活動會實作 CapabilityClient.OnCapabilityChangedListener
。
下文將比較類別定義。
以下是使用 11.8.0 版 Google Play 服務前的程式碼片段:
Kotlin
class MainActivity : Activity(), GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, DataApi.DataListener, MessageApi.MessageListener, CapabilityApi.CapabilityListener
Java
public class MainActivity extends Activity implements ConnectionCallbacks, OnConnectionFailedListener, DataApi.DataListener, MessageApi.MessageListener, CapabilityApi.CapabilityListener
以下是使用 11.8.0 版 Google Play 服務後的程式碼片段:
Kotlin
class MainActivity : Activity(), DataClient.OnDataChangedListener, MessageClient.OnMessageReceivedListener, CapabilityClient.OnCapabilityChangedListener
Java
public class MainActivity extends Activity implements DataClient.OnDataChangedListener, MessageClient.OnMessageReceivedListener, CapabilityClient.OnCapabilityChangedListener
移除和新增事件監聽器
新的用戶端物件會在
GoogleApi
例項之間快取及分享,所以沒有必要保留成員變數;此外,製作用戶端不需耗費太多資源,用戶端也不會遺失事件監聽器。
以下程式碼片段取自修改過後的 Wear Data Layer 示例:
Kotlin
override fun onResume() { super.onResume() Wearable.getDataClient(this).addListener(this) Wearable.getMessageClient(this).addListener(this) Wearable.getCapabilityClient(this) .addListener( this, Uri.parse("wear://"), CapabilityClient.FILTER_REACHABLE ) } override fun onPause() { super.onPause() Wearable.getDataClient(this).removeListener(this) Wearable.getMessageClient(this).removeListener(this) Wearable.getCapabilityClient(this).removeListener(this) }
Java
@Override protected void onResume() { super.onResume(); Wearable.getDataClient(this).addListener(this); Wearable.getMessageClient(this).addListener(this); Wearable.getCapabilityClient(this) .addListener( this, Uri.parse("wear://"), CapabilityClient.FILTER_REACHABLE); } @Override protected void onPause() { super.onPause(); Wearable.getDataClient(this).removeListener(this); Wearable.getMessageClient(this).removeListener(this); Wearable.getCapabilityClient(this).removeListener(this); }
使用 Tasks API 要求資訊
您可能需要求事件監聽器以外的資訊,監聽器才能在資料變動時更新應用程式。在這種情況下,請利用
DataClient
這類用戶端、搭配 Tasks API 以及結果類別 (當做 Task<ResultType>
) 提出要求。
舉例來說,正如 Wear Data Layer 範例所示,您可以使用 Tasks API 尋找具有任何指定功能的已連線節點:
Kotlin
private fun showNodes(vararg capabilityNames: String) { Wearable.getCapabilityClient(this) .getAllCapabilities(CapabilityClient.FILTER_REACHABLE).apply { addOnSuccessListener { capabilityInfoMap -> val nodes: Set<Node> = capabilityInfoMap .filter { capabilityNames.contains(it.key) } .flatMap { it.value.nodes } .toSet() showDiscoveredNodes(nodes) } } } private fun showDiscoveredNodes(nodes: Set<Node>) { val nodesList: Set<String> = nodes.map { it.displayName }.toSet() val msg: String = if (nodesList.isEmpty()) { Log.d(TAG, "Connected Nodes: No connected device was found for the given capabilities") getString(R.string.no_device) } else { Log.d(TAG, "Connected Nodes: ${nodesList.joinToString(separator = ", ")}") getString(R.string.connected_nodes, nodesList) } Toast.makeText(this@MainActivity, msg, Toast.LENGTH_LONG).show() }
Java
private void showNodes(final String... capabilityNames) { Task<Map<String, CapabilityInfo>> capabilitiesTask = Wearable.getCapabilityClient(this) .getAllCapabilities(CapabilityClient.FILTER_REACHABLE); capabilitiesTask.addOnSuccessListener(new OnSuccessListener<Map<String, CapabilityInfo>>() { @Override public void onSuccess(Map<String, CapabilityInfo> capabilityInfoMap) { Set<Node> nodes = new HashSet<>(); if (capabilityInfoMap.isEmpty()) { showDiscoveredNodes(nodes); return; } for (String capabilityName : capabilityNames) { CapabilityInfo capabilityInfo = capabilityInfoMap.get(capabilityName); if (capabilityInfo != null) { nodes.addAll(capabilityInfo.getNodes()); } } showDiscoveredNodes(nodes); } }); } private void showDiscoveredNodes(Set<Node> nodes) { List<String> nodesList = new ArrayList<>(); for (Node node : nodes) { nodesList.add(node.getDisplayName()); } LOGD(TAG, "Connected Nodes: " + (nodesList.isEmpty() ? "No connected device was found for the given capabilities" : TextUtils.join(",", nodesList))); String msg; if (!nodesList.isEmpty()) { msg = getString(R.string.connected_nodes, TextUtils.join(", ", nodesList)); } else { msg = getString(R.string.no_device); } Toast.makeText(MainActivity.this, msg, Toast.LENGTH_LONG).show(); }
如果想瞭解其他使用 Wearable 和 Tasks API 的程式碼,請參閱 Wear Data Layer 示例。以在 UI 執行緒或服務中使用繁重工作的情況為例,您也可以選擇其他做法。以下範例展示如何在工作上進行封鎖,並同步取得結果:
Kotlin
override fun doInBackground(vararg params: Asset): Bitmap? { if (params.isNotEmpty()) { val asset = params[0] val getFdForAssetResponseTask: Task<DataClient.GetFdForAssetResponse> = Wearable.getDataClient(applicationContext).getFdForAsset(asset) return try { // Block on a task and get the result synchronously. This is generally done // when executing a task inside a separately managed background thread. Doing // this on the main (UI) thread can cause your application to become // unresponsive. val getFdForAssetResponse: DataClient.GetFdForAssetResponse = Tasks.await(getFdForAssetResponseTask) getFdForAssetResponse.inputStream?.let { assetInputStream -> BitmapFactory.decodeStream(assetInputStream) } ?: run { Log.w(TAG, "Requested an unknown Asset.") null } } catch (exception: ExecutionException) { Log.e(TAG, "Failed retrieving asset, Task failed: $exception") return null } catch (exception: InterruptedException) { Log.e(TAG, "Failed retrieving asset, interrupt occurred: $exception") return null } } else { Log.e(TAG, "Asset must be non-null") return null } } override fun onPostExecute(bitmap: Bitmap?) { bitmap?.also { Log.d(TAG, "Setting background image on second page..") moveToPage(1) assetFragment.setBackgroundImage(it) } }
Java
@Override protected Bitmap doInBackground(Asset... params) { if (params.length > 0) { Asset asset = params[0]; Task<DataClient.GetFdForAssetResponse> getFdForAssetResponseTask = Wearable.getDataClient(getApplicationContext()).getFdForAsset(asset); try { // Block on a task and get the result synchronously. This is generally done // when executing a task inside a separately managed background thread. Doing // this on the main (UI) thread can cause your application to become // unresponsive. DataClient.GetFdForAssetResponse getFdForAssetResponse = Tasks.await(getFdForAssetResponseTask); InputStream assetInputStream = getFdForAssetResponse.getInputStream(); if (assetInputStream != null) { return BitmapFactory.decodeStream(assetInputStream); } else { Log.w(TAG, "Requested an unknown Asset."); return null; } } catch (ExecutionException exception) { Log.e(TAG, "Failed retrieving asset, Task failed: " + exception); return null; } catch (InterruptedException exception) { Log.e(TAG, "Failed retrieving asset, interrupt occurred: " + exception); return null; } } else { Log.e(TAG, "Asset must be non-null"); return null; } } @Override protected void onPostExecute(Bitmap bitmap) { if (bitmap != null) { LOGD(TAG, "Setting background image on second page.."); moveToPage(1); assetFragment.setBackgroundImage(bitmap); } }