Memigrasikan aplikasi Wear ke GoogleApi

Mulai layanan Google Play versi 11.8.0, aplikasi Wear OS harus bermigrasi dari class GoogleApiClient dan sebagai gantinya gunakan objek klien yang didasarkan pada class GoogleApi.

Penggunaan GoogleApi memudahkan penyiapan operasi asinkron. Misalnya, seperti yang dijelaskan dalam pengantar Tasks API, Anda bisa mendapatkan objek Task sebagai ganti objek PendingResult.

Halaman ini menyertakan:

  • Tabel komponen pengganti
  • Contoh update aplikasi yang ada untuk menggunakan Tasks API

Catatan: Update ini tidak berlaku untuk aplikasi Wear OS versi China, yang umumnya menggunakan layanan Google Play versi 10.2.0.

Catatan: API ini saat ini hanya tersedia di ponsel Android dan smartwatch Wear OS yang disambungkan dengan ponsel Android. Untuk smartwatch Wear OS yang disambungkan dengan ponsel iOS, aplikasi dapat mengkueri API berbasis cloud lainnya jika konektivitas Internet tersedia.

Penggantian untuk komponen yang tidak digunakan lagi

Saat Anda menggunakan class yang memperluas class GoogleApi, seperti DataClient dan MessageClient, SDK layanan Google Play mengelola koneksi ke layanan Google Play untuk Anda.

Aplikasi yang menggunakan class pengganti di bawah ini tidak perlu membuat dan mengelola objek GoogleApiClient. Lihat juga Mengakses Google API dan halaman referensi untuk class Wearable.

Tabel berikut berisi komponen yang sudah tidak digunakan lagi dan penggantinya:

Komponen yang tidak digunakan lagi Komponen pengganti
CapabilityApi CapabilityClient
Channel ChannelClient.Channel
ChannelApi ChannelClient
DataApi DataClient
MessageApi MessageClient
NodeApi NodeClient

Selain itu, perhatikan hal-hal berikut:

Contoh migrasi untuk aplikasi Wear

Sebagai contoh migrasi, cuplikan kode di bawah ini mengilustrasikan bagaimana contoh Lapisan Data Wear, yang menggunakan Data Layer API, diupdate untuk layanan Google Play versi 11.8.0. Jika aplikasi Anda memiliki modul ponsel, update-nya mungkin serupa dengan yang ada pada modul Wear.

Mengupdate dependensi pada layanan Google Play

Karena aplikasi Anda mungkin bergantung pada versi layanan Google Play yang lebih lama, update dependensi berikut dalam file build.gradle dari modul Wear Anda:

dependencies {
...
compile 'com.google.android.gms:play-services-wearable:11.8.0'
}

Mengupdate pernyataan impor aplikasi Anda

Impor class yang diperlukan, termasuk class di Tasks API.

Misalnya, sebelumnya contoh Lapisan Data Wear menyertakan pernyataan impor berikut dalam file MainActivity.java. Pernyataan import ini harus dihapus:

Kotlin

...
import com.google.android.gms.common.api.GoogleApiClient
...

Java

...
import com.google.android.gms.common.api.GoogleApiClient;
...

Dalam contoh Lapisan Data Wear, pernyataan import seperti di atas dapat diganti dengan yang berikut ini (yang kedua adalah untuk menangani pengecualian tugas):

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

Mengimplementasikan antarmuka klien baru

Hapus semua penggunaan class GoogleApiClient dan antarmuka terkait (ConnectionCallbacks, OnConnectionFailedListener, dll.), lalu ganti implementasi Pemroses lainnya dengan versi baru. Metode aktual untuk mengganti umumnya memiliki nama yang sama seperti sebelumnya, sehingga perubahan utama mirip dengan contoh di bawah ini.

Sebagai contoh, aktivitas utama contoh Lapisan Data Wear (seperti yang ditunjukkan dalam diff pada GitHub) telah mengimplementasikan antarmuka CapabilityApi.CapabilityListener. Selanjutnya, aktivitas utama mengimplementasikan CapabilityClient.OnCapabilityChangedListener.

Di bawah ini adalah perbandingan definisi class.

Berikut ini cuplikan sebelum penggunaan layanan Google Play versi 11.8.0:

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

Berikut ini cuplikan setelah penggunaan layanan Google Play versi 11.8.0:

Kotlin

class MainActivity :
        Activity(),
        DataClient.OnDataChangedListener,
        MessageClient.OnMessageReceivedListener,
        CapabilityClient.OnCapabilityChangedListener

Java

public class MainActivity extends Activity implements
  DataClient.OnDataChangedListener,
  MessageClient.OnMessageReceivedListener,
  CapabilityClient.OnCapabilityChangedListener

Menghapus dan menambahkan pemroses

Objek klien baru di-cache dan digunakan bersama oleh instance GoogleApi sehingga tidak perlu menyimpan variabel anggota; pembuatan klien tidaklah mahal dan klien tidak akan kehilangan pemrosesnya.

Di bawah ini adalah cuplikan dari revisi contoh Lapisan Data Wear:

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

Meminta informasi dengan Tasks API

Anda mungkin ingin meminta informasi di luar pemroses yang mengupdate aplikasi saat ada perubahan data. Dalam kasus semacam itu, buat permintaan menggunakan klien seperti DataClient, bersama dengan Tasks API dan class hasil (yaitu, sebagai Task<ResultType>).

Misalnya, seperti yang ditunjukkan dalam contoh Lapisan Data Wear, Anda dapat menggunakan Tasks API untuk menemukan node yang terhubung dengan kapabilitas yang diberikan:

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

Untuk kode tambahan yang menggunakan Wearable dan Tasks API, lihat contoh Lapisan Data Wear. Dan sebagai contoh menggunakan tugas berat dari UI thread atau dalam layanan, tersedia opsi lain. Berikut adalah contoh cara memblokir pada tugas dan mendapatkan hasil secara sinkron:

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