ارسال درخواست های دسته ای

این سند نشان می‌دهد که چگونه می‌توانید تماس‌های API را با هم دسته‌بندی کنید تا تعداد اتصال‌هایی که مشتری شما باید برقرار کند را کاهش دهید.

این سند به طور خاص در مورد ایجاد یک درخواست دسته ای با استفاده از کتابخانه مشتری جاوا است. یک مثال اولیه نیز در Google API Client Library برای دات نت موجود است. سیستم دسته‌ای برای Google Play EMM API از دستور HTTP مشابه سیستم پردازش دسته‌ای OData استفاده می‌کند.

نمای کلی

هر درخواستی که مشتری شما از طریق Google Play EMM API ارائه می‌کند، منجر به مقدار معینی سربار می‌شود. Google Play EMM API از دسته‌بندی پشتیبانی می‌کند تا به مشتری شما اجازه دهد چندین تماس API را در یک درخواست قرار دهد.

در اینجا چند نمونه از موقعیت هایی وجود دارد که ممکن است بخواهید در آنها از دسته بندی استفاده کنید:

  • دامنه ای به تازگی ثبت شده است و اکنون داده های زیادی برای آپلود دارد.
  • زمانی که برنامه شما آفلاین بود، یک کاربر تغییراتی در داده‌ها ایجاد کرد، بنابراین برنامه شما باید مقدار زیادی از داده‌های محلی را با سرور همگام‌سازی کند.

در مواردی از این قبیل، به جای ارسال هر تماس جداگانه، می توانید آنها را در یک درخواست واحد گروه بندی کنید. حتی می‌توانید درخواست‌های چند کاربر یا چندین API Google را گروه‌بندی کنید.

با این حال، شما محدود به 1000 تماس در یک درخواست دسته ای هستید. اگر نیاز به تماس بیشتر از آن دارید، از چندین درخواست دسته ای استفاده کنید.

جزئیات دسته

یک درخواست دسته ای شامل چندین تماس API است که در یک درخواست JSON-RPC ترکیب شده اند. این بخش نحو درخواست دسته ای را با یک مثال در بخش زیر به تفصیل شرح می دهد.

توجه : مجموعه‌ای از n درخواست که با هم جمع شده‌اند به عنوان n درخواست به حساب می‌آیند، نه به عنوان یک درخواست. درخواست دسته ای قبل از پردازش به مجموعه ای از درخواست ها تقسیم می شود.

فرمت درخواست دسته ای

کتابخانه سرویس گیرنده جاوا شامل تماس هایی برای ایجاد درخواست برای هر تماس Google Play EMM API است. به عنوان مثال، برای فهرست کردن همه برنامه های نصب شده روی یک دستگاه، از موارد زیر استفاده کنید:

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 جداگانه است.

پاسخ به درخواست دسته ای

سرور هر درخواست جداگانه را اجرا می کند و نتیجه را در یک پاسخ واحد از یک آرایه واحد گروه بندی می کند. کتابخانه مشتری این پاسخ را به پاسخ های جداگانه تقسیم می کند و هر یک به تابع callback ارسال شده به 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();
  }
}