ย้ายข้อมูลแอป Wear ไปยัง GoogleApi

เริ่มต้นด้วย version 11.8.0 ของบริการ Google Play แอป Wear OS ควรย้ายข้อมูลออกไป จาก GoogleApiClient และใช้ออบเจ็กต์ไคลเอ็นต์ที่อ้างอิงคลาส GoogleApi แทน

การใช้ GoogleApi จะช่วยให้ตั้งค่าการดำเนินการแบบอะซิงโครนัสได้ง่ายขึ้น ตัวอย่างเช่น ตามที่อธิบายไว้ในข้อมูลเบื้องต้นเกี่ยวกับ Tasks API คุณสามารถรับออบเจ็กต์ Task แทน PendingResult ออบเจ็กต์

หน้านี้ประกอบด้วยเนื้อหาต่อไปนี้

  • ตารางองค์ประกอบการแทนที่
  • ตัวอย่างการอัปเดตแอปที่มีอยู่เพื่อใช้ Tasks API

หมายเหตุ: การอัปเดตนี้ไม่มีผลกับแอป Wear OS สำหรับ จีน ซึ่งโดยทั่วไปจะใช้บริการ Google Play เวอร์ชัน 10.2.0

หมายเหตุ: ปัจจุบัน API นี้ใช้ได้เฉพาะในโทรศัพท์ Android เท่านั้น และนาฬิกา Wear OS ที่จับคู่กับโทรศัพท์ Android สำหรับนาฬิกา Wear OS ที่จับคู่กับ iOS โทรศัพท์ แอปจะสามารถค้นหา API อื่นๆ ในระบบคลาวด์ได้หากมีการเชื่อมต่ออินเทอร์เน็ต

การแทนที่คอมโพเนนต์ที่เลิกใช้งานแล้ว

เมื่อคุณใช้ชั้นเรียนที่ขยายคลาส GoogleApi เช่น DataClient และ MessageClient SDK บริการ Google Play จะจัดการ การเชื่อมต่อกับบริการ Google Play ให้กับคุณ

แอปที่ใช้คลาสทดแทนด้านล่างไม่จำเป็นต้องสร้างและ จัดการ GoogleApiClient ออบเจ็กต์ โปรดดูเพิ่มเติมที่การเข้าถึง Google APIs และ หน้าอ้างอิงสำหรับชั้นเรียนอุปกรณ์ที่สวมใส่ได้

ตารางต่อไปนี้ประกอบด้วยคอมโพเนนต์ที่เลิกใช้งานและคอมโพเนนต์ การแทนที่:

คอมโพเนนต์ที่เลิกใช้งานแล้ว คอมโพเนนต์แทนที่
CapabilityApi CapabilityClient
Channel ChannelClient.Channel
ChannelApi ChannelClient
DataApi DataClient
MessageApi MessageClient
NodeApi NodeClient

และโปรดทราบข้อมูลต่อไปนี้

ตัวอย่างการย้ายข้อมูลสำหรับแอป Wear

สำหรับตัวอย่างการย้ายข้อมูล ข้อมูลโค้ดด้านล่างจะแสดงวิธีการ ข้อมูล Wear ตัวอย่างเลเยอร์ซึ่งใช้ Data Layer API อัปเดตสำหรับ เวอร์ชัน 11.8.0 ของบริการ Google Play หากแอปของคุณมีโมดูลโทรศัพท์ อาจคล้ายกับการอัปเดตสำหรับโมดูล Wear

อัปเดตทรัพยากร Dependency ในบริการ Google Play

เนื่องจากแอปของคุณอาจใช้บริการ Google Play เวอร์ชันก่อนหน้า อัปเดตทรัพยากร Dependency ต่อไปนี้ในไฟล์ build.gradle ของ โมดูล Wear

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

อัปเดตข้อความการนำเข้าของแอป

นำเข้าคลาสที่จำเป็น รวมถึงคลาสใน Tasks API

ตัวอย่างเช่น เดิมคือชั้นข้อมูล Wear ตัวอย่างมีคำสั่งการนำเข้าต่อไปนี้ใน MainActivity.java ไฟล์ ใบแจ้งยอด import นี้ ควรลบออก:

Kotlin

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

Java

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

ในWear ตัวอย่างชั้นข้อมูล, คำสั่ง 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 (ตามที่ระบุไว้ในความแตกต่าง เมื่อ GitHub) ได้ติดตั้งใช้งาน ตัวอย่างเช่น อินเทอร์เฟซ CapabilityApi.CapabilityListener แต่ตอนนี้ การใช้กิจกรรมหลัก CapabilityClient.OnCapabilityChangedListener

ด้านล่างนี้คือการเปรียบเทียบคำจำกัดความของชั้นเรียน

ต่อไปนี้คือตัวอย่างก่อนการใช้ Google Play เวอร์ชัน 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

ตัวอย่างการใช้งาน Google Play เวอร์ชัน 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

นำ Listener ออกและเพิ่ม

ออบเจ็กต์ไคลเอ็นต์ใหม่จะได้รับการแคชและแชร์ระหว่างอินสแตนซ์ GoogleApi รายการ จึงไม่จำเป็นต้องเก็บสมาชิกไว้ ตัวแปร; สามารถสร้างลูกค้าที่มีต้นทุนไม่แพง และไม่สูญเสียลูกค้า Listener ของคุณ

ด้านล่างเป็นตัวอย่างข้อมูลจากชั้นข้อมูล 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);
}

ขอข้อมูลด้วย Tasks API

คุณอาจต้องขอข้อมูลนอกผู้ฟังที่อัปเดต แอปของคุณเมื่อมีการเปลี่ยนแปลงข้อมูล ในกรณีดังกล่าว ให้ส่งคำขอโดยใช้ ไคลเอ็นต์ เช่น DataClient ร่วมกับ Tasks API และ คลาสผลลัพธ์ (ซึ่งก็คือ Task<ResultType>)

ตัวอย่างเช่น ดังที่แสดงในชั้นข้อมูล Wear ตัวอย่าง คุณสามารถใช้ 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();
}

สำหรับรหัสเพิ่มเติมที่ใช้ API ที่สวมใส่ได้และ API ของ Tasks โปรดดู ชั้นข้อมูล Wear ตัวอย่าง และเป็นตัวอย่างของการใช้งานหนักๆ นอกชุดข้อความ 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);
  }
}