إرسال طلبات مجمّعة

يعرض هذا المستند كيفية تجميع طلبات البيانات من واجهة برمجة التطبيقات معًا لتقليل عدد الاتصالات التي يجب على العميل إجراؤها.

يتناول هذا المستند تحديدًا كيفية تقديم طلب مجمّع باستخدام مكتبة عميل جافا. يتوفر مثال أساسي أيضًا في مكتبة عميل واجهة برمجة تطبيقات Google لـ NET. ويستخدم النظام المجمّع لواجهة برمجة تطبيقات إدارة خدمات الأجهزة الجوّالة للمؤسسات (EMM) في Google Play بنية HTTP نفسها التي يستخدمها نظام معالجة بيانات OData.

نظرة عامة

ينتج عن كل طلب يجريه العميل عبر واجهة برمجة تطبيقات إدارة الخدمات الجوّالة للمؤسسات في Google Play مقدار معين من النفقات العامة. تتيح واجهة برمجة التطبيقات لإدارة الخدمات الجوّالة للمؤسسات (EMM) في Google Play إمكانية التجميع، وذلك للسماح للعميل بتنفيذ عدة طلبات بيانات من واجهة برمجة التطبيقات في طلب واحد.

في ما يلي بعض الأمثلة على الحالات التي قد تحتاج فيها إلى استخدام التجميع:

  • تم تسجيل النطاق للتو ولديه الآن الكثير من البيانات للتحميل.
  • أجرى مستخدم تغييرات على البيانات أثناء عدم اتصال التطبيق، لذلك يحتاج التطبيق إلى مزامنة قدر كبير من البيانات المحلية مع الخادم.

في مثل هذه الحالات، بدلاً من إرسال كل مكالمة على حدة، يمكنك تجميعها معًا في طلب واحد. بل ويمكنك تجميع الطلبات لعدة مستخدمين أو لعدة واجهات برمجة تطبيقات Google.

ومع ذلك، تقتصر على 1000 مكالمة في طلب واحد مجمّع. إذا كنت بحاجة إلى إجراء المزيد من المكالمات، يمكنك استخدام طلبات مجمّعة متعددة.

تفاصيل الدفعة

يتكون طلب الدفعة من طلبات بيانات من واجهة برمجة تطبيقات متعددة مجمّعة في طلب JSON-RPC واحد. يصف هذا القسم بنية الطلب المجمّع بالتفصيل، مع إضافة مثال في القسم التالي.

ملاحظة: يتم احتساب مجموعة من الطلبات المجمّعة التي يبلغ عددها n ضمن حدّ الاستخدام كطلبات n، وليس كطلب واحد. ويتم تقسيم طلب الدفعة إلى مجموعة من الطلبات قبل المعالجة.

تنسيق الطلب المجمّع

تحتوي مكتبة عميل جافا على طلبات لإنشاء طلبات لكل طلب بيانات من واجهة برمجة التطبيقات لإدارة الخدمات الجوّالة للمؤسسات في Google Play. على سبيل المثال، لإدراج جميع التطبيقات المثبتة على الجهاز، يمكنك استخدام ما يلي:

AndroidEnterprise enterprise = ...;
InstallsListResponse response = enterprise.installs().list(enterpriseId, userId, deviceId)
  .execute();

هناك مكالمة batch() إضافية يمكنها وضع عدة طلبات في قائمة الانتظار، كما هو موضح هنا:

AndroidEnterprise enterprise = ...;
BatchRequest batchRequest = enterprise.batch();
enterprise.installs().list(enterpriseId, userId, deviceId1).queue(batchRequest, callback1);
enterprise.installs().list(enterpriseId, userId, deviceId2).queue(batchRequest, callback2);
enterprise.installs().list(enterpriseId, userId, deviceId3).queue(batchRequest, callback3);
batchRequest.execute();
عند استدعاء batchRequest.execute()، يتم إرسال جميع الطلبات في قائمة الانتظار في وقت واحد إلى الخادم كمصفوفة JSON. ويطبق الخادم معامِلات طلب البحث وعناوين الطلب الخارجية (حسب الاقتضاء) على كل جزء، ثم يتعامل مع كل جزء كما لو كان طلب JSON منفصلاً.

الرد على طلب مجمّع

ينفِّذ الخادم كل طلب منفصل، ويجمِّع النتيجة في رد واحد من مصفوفة واحدة. تقسّم مكتبة العميلة هذه الاستجابة إلى ردود فردية، ويتم إرسال كل رد منها إلى دالة رد الاتصال التي تم تمريرها إلى queue(). رد الاتصال هو واجهة تحدد طريقة للفشل وطريقة للنجاح. على سبيل المثال، سيتم تنفيذ السياسة callback1 على النحو التالي:

private class InstallsCallback implements JsonBatchCallback<InstallsListResponse> {

  @Override
  public void onSuccess(InstallsListResponse response, HttpHeaders responseHeaders) {
    ...
  }

  @Override
  public void onFailure(GoogleJsonError e, HttpHeaders responseHeaders) {
    ...
  }
}

ملاحظة: قد يجري الخادم مكالماتك بأي ترتيب، لذلك لا تعتمد على تلقي النتائج بالترتيب المحدد في طلبك. إذا كنت تريد ضمان إجراء مكالمتين بترتيب معين، فلا يمكنك إرسالهما في طلب واحد، بل يمكنك إرسال الطلب الأول وحده، وانتظر الرد قبل إرسال الطلب الثاني.

مثال على طلب مجمّع

يوضح المثال التالي كيفية عرض قائمة بجميع التطبيقات المثبتة على جميع أجهزة المستخدمين. تُستخدَم الاستدعاءات الأولى للحصول على رقم تعريف المؤسسة والمستخدم، وبالتالي يجب تنفيذها بشكل تسلسلي. بعد الحصول على جميع أرقام تعريف الأجهزة باستخدام enterprise.devices().list()، يمكننا تقديم طلب مجمّع لاسترداد جميع التطبيقات على جميع أجهزة المستخدم دفعة واحدة.

package com.google.playenterprise.example;

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.batch.BatchRequest;
import com.google.api.client.googleapis.batch.json.JsonBatchCallback;
import com.google.api.client.googleapis.json.GoogleJsonError;
import com.google.api.client.http.HttpHeaders;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson.JacksonFactory;
import com.google.api.services.androidenterprise.AndroidEnterprise;
import com.google.api.services.androidenterprise.AndroidEnterprise.Installs;
import com.google.api.services.androidenterprise.AndroidEnterpriseScopes;
import com.google.api.services.androidenterprise.model.Device;
import com.google.api.services.androidenterprise.model.DevicesListResponse;
import com.google.api.services.androidenterprise.model.Enterprise;
import com.google.api.services.androidenterprise.model.Install;
import com.google.api.services.androidenterprise.model.InstallsListResponse;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * Lists all the apps installed on all devices of a given user.
 */
public class ListAllInstalls {
  private AndroidEnterprise enterprise;
  private final List<String> installList = new ArrayList<>();

  public static void main(String[] argv) throws Exception {
    if (argv.length != 2) {
      throw new IllegalArgumentException("Usage: ListAllInstalls email jsonFilename");
    } else if (!argv[0].contains("@")) {
      throw new IllegalArgumentException("First parameter should be a valid email.");
    }
    new ListAllInstalls().run(argv[0], argv[1]);
  }

  private void run(String userEmail, String jsonKeyPath) throws IOException {
    enterprise = createAndroidEnterprise(jsonKeyPath);

    // Get the enterprise id, user id, and user devices.
    String domain = userEmail.split("@")[1];
    List<Enterprise> results = enterprise.enterprises().list(domain).execute().getEnterprise();
    if (results.isEmpty()) {
      throw new RuntimeException("No enterprise found.");
    }
    String enterpriseId = results.get(0).getId();
    String userId = enterprise
        .users()
        .list(enterpriseId, userEmail)
        .execute()
        .getUser()
        .get(0)
        .getId();
    List<Device> devices = getAllDevices(enterpriseId, userId);

    // Batch all calls to get installs on all user devices.
    gatherAllInstalls(enterpriseId, userId, devices);

    for (String entry : installList) {
      // Do something.
      System.out.println(entry);
    }
  }

  private List<Device> getAllDevices(String enterpriseId, String userId) throws IOException {
    DevicesListResponse devices = enterprise.devices().list(enterpriseId, userId).execute();
    return devices.getDevice();
  }

  private void gatherAllInstalls(String enterpriseId, String userId, List<Device> devices)
      throws IOException {
    BatchRequest batchRequest = enterprise.batch();
    for (Device device : devices) {
      Installs.List list = enterprise
          .installs().list(enterpriseId, userId, device.getAndroidId());
      // Each callback can take the specifics of the associated request in its constructor.
      list.queue(batchRequest, new InstallsCallback(device.getAndroidId()));
    }
    // Executes all the queued requests and their callbacks, single-threaded.
    batchRequest.execute();
  }

  private class InstallsCallback extends JsonBatchCallback<InstallsListResponse> {
    private final String androidId;

    InstallsCallback(String androidId) {
      this.androidId = androidId;
    }

    @Override
    public void onSuccess(InstallsListResponse response, HttpHeaders responseHeaders) {
      for (Install install : response.getInstall()) {
        installList.add(androidId + "," + install.getProductId());
      }
    }

    @Override
    public void onFailure(GoogleJsonError e, HttpHeaders responseHeaders) {
      throw new RuntimeException("Error fetching a device");
    }
  }

  private AndroidEnterprise createAndroidEnterprise(String jsonKeyPath) throws IOException {
    HttpTransport httpTransport = new NetHttpTransport();
    JsonFactory jsonFactory = new JacksonFactory();

    InputStream is = new BufferedInputStream(new FileInputStream(jsonKeyPath));
    final Credential credential = GoogleCredential.fromStream(is, httpTransport, jsonFactory)
        .createScoped(AndroidEnterpriseScopes.all());

    HttpRequestInitializer httpRequestInitializer = new HttpRequestInitializer() {
      @Override
      public void initialize(HttpRequest request) throws IOException {
        credential.initialize(request);
      }
    };
    return new AndroidEnterprise.Builder(httpTransport, jsonFactory, httpRequestInitializer)
        .build();
  }
}