نقل تطبيقات Wear إلى GoogleApi

البدء بـ الإصدار الإصدار 11.8.0 من "خدمات Google Play"، يجب نقل تطبيقات Wear OS نهائيًا من GoogleApiClient الفئة واستخدام كائنات العميل المستندة إلى الفئة GoogleApi بدلاً من ذلك.

يؤدي استخدام GoogleApi إلى تسهيل إعداد العمليات غير المتزامنة. على سبيل المثال، كما هو موضح في مقدمة عن واجهة برمجة تطبيقات "مهام Google"، يمكنك الحصول على كائن Task بدلاً من الكائن PendingResult.

تتضمن هذه الصفحة ما يلي:

  • جدول مكونات الاستبدال
  • مثال على تحديث تطبيق حالي لاستخدام Tasks API

ملاحظة: لا ينطبق هذا التحديث على تطبيقات Wear OS في الصين التي تستخدم بشكل عام الإصدار 10.2.0 من "خدمات Google Play"

ملاحظة: تتوفّر واجهة برمجة التطبيقات هذه حاليًا على هواتف Android فقط. وساعات Wear OS المقترنة بهواتف Android. لساعات Wear OS المقترنة بنظام iOS الهواتف، يمكن للتطبيقات الاستعلام عن واجهات برمجة التطبيقات الأخرى التي تستند إلى السحابة الإلكترونية في حالة توفر الاتصال بالإنترنت.

استبدالات المكوّنات التي تم إيقافها نهائيًا

عند استخدام الصفوف التي تُوسّع فئة 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.

تعديل الاعتمادية على "خدمات Google Play"

بما أنّ تطبيقك قد يعتمد على إصدار سابق من "خدمات Google Play"، قم بتعديل التبعية التالية في ملف 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 وما إلى ذلك) واستبدال الآخر عمليات التنفيذ للمستمعين مع إصداراتهم الجديدة. الطرق الفعلية لها نفس الأسماء كما في السابق، لذا فإن التغيير الرئيسي مشابه للمثال أدناه.

النشاط الرئيسي لعينة طبقة بيانات Wear (كما هو موضح في الاختلاف في 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 OS المعدَّلة النموذج:

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

طلب معلومات باستخدام واجهة برمجة تطبيقات "مهام Google"

قد تحتاج إلى طلب معلومات من خارج المستمعين يعدّلون المحتوى. تطبيقك عند حدوث تغيير في البيانات. في هذه الحالات، يمكنك تقديم طلب باستخدام عميل مثل 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();
}

للحصول على رمز إضافي يستخدم واجهات برمجة تطبيقات "المهام القابلة للارتداء" و"مهام Google"، يُرجى الاطّلاع على طبقة بيانات Wear عينة. وكمثال على استخدام المهام الصعبة خارج سلسلة واجهة المستخدم أو في إحدى الخدمات، يتوفّر خيار آخر. فيما يلي مثال على طريقة حظر الوصول إلى مهمة والحصول على النتيجة بشكل متزامن:

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