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.

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. Juga lihat 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, diperbarui untuk layanan Google Play versi 11.8.0. Jika aplikasi Anda memiliki modul ponsel, update-nya mungkin serupa dengan yang ada pada modul Wear.

Memperbarui dependensi pada layanan Google Play

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

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

Memperbarui 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 diganti dengan, misalnya, 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.

Aktivitas utama contoh Lapisan Data Wear (seperti yang ditunjukkan dalam perbedaan pada GitHub) telah diimplementasikan, contoh, 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 asinkron:

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