ส่งคําขอแบบกลุ่ม

เอกสารนี้แสดงวิธีจัดกลุ่มการเรียก API เพื่อลดจํานวนการเชื่อมต่อที่ไคลเอ็นต์ต้องสร้าง

เอกสารนี้เกี่ยวกับการสร้างคําขอแบบกลุ่มโดยใช้ไลบรารีของไคลเอ็นต์ Java ตัวอย่างพื้นฐานยังพร้อมใช้งานในไลบรารีของไคลเอ็นต์ Google API สําหรับ .NET ระบบกลุ่มสําหรับ Google Play EMM API ใช้ไวยากรณ์ HTTP เดียวกันกับระบบการประมวลผลกลุ่มข้อมูล

ภาพรวม

คําขอแต่ละรายการที่ไคลเอ็นต์สร้างผ่าน API ของ Google Play EMM ส่งผลให้มีค่าใช้จ่ายในการดําเนินการจํานวนหนึ่ง Google Play EMM API รองรับการทํางานแบบกลุ่ม เพื่ออนุญาตให้ไคลเอ็นต์เรียกใช้ API หลายรายการในคําขอเดียว

ต่อไปนี้คือตัวอย่างของสถานการณ์ที่คุณอาจต้องการใช้การทํางานแบบกลุ่ม

  • โดเมนเพิ่งลงทะเบียนและมีข้อมูลที่อัปโหลดมากมาย
  • ผู้ใช้เปลี่ยนแปลงข้อมูลขณะที่แอปพลิเคชันของคุณออฟไลน์ แอปพลิเคชันของคุณจึงต้องซิงค์ข้อมูลท้องถิ่นจํานวนมากกับเซิร์ฟเวอร์

ในกรณีเช่นนี้ คุณจะจัดกลุ่มการโทรเหล่านั้นไว้ในคําขอเดียวแทนการส่งแต่ละสายได้ และยังจัดกลุ่มคําขอสําหรับผู้ใช้หลายรายหรือสําหรับ Google API หลายรายการได้อีกด้วย

อย่างไรก็ตาม คุณจะโทรได้ไม่เกิน 1,000 คําขอต่อคําขอ 1 รายการ หากต้องการโทรออกมากกว่านั้น ให้ใช้คําขอแบบกลุ่มหลายรายการ

รายละเอียดกลุ่ม

คําขอแบบกลุ่มประกอบด้วยการเรียก API หลายรายการรวมกันเป็นคําขอ JSON-RPC 1 รายการ ส่วนนี้อธิบายไวยากรณ์ของคําขอแบบกลุ่มโดยละเอียดโดยมีตัวอย่างในส่วนต่อไปนี้

หมายเหตุ: ชุดคําขอ n ที่จัดกลุ่มไว้ด้วยกันจะนับรวมในขีดจํากัดการใช้งานเป็นคําขอ n ไม่ใช่หนึ่งคําขอ คําขอแบบกลุ่มจะแยกออกเป็นชุดคําขอก่อนการประมวลผล

รูปแบบคําขอแบบกลุ่ม

ไลบรารีของไคลเอ็นต์ Java มีการเรียกใช้การสร้างคําขอสําหรับการเรียก 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 แยกต่างหาก

การตอบกลับคําขอแบบกลุ่ม

เซิร์ฟเวอร์จะเรียกใช้คําขอแต่ละรายการแยกกัน และจัดกลุ่มผลลัพธ์เป็นการตอบสนองเดียวจากอาร์เรย์เดียว ไลบรารีของไคลเอ็นต์จะแยกการตอบกลับนี้ออกเป็นการตอบกลับแต่ละรายการ และโฆษณาแต่ละรายการจะส่งไปยังฟังก์ชันเรียกกลับที่ส่งไปยัง queue() โค้ดเรียกกลับคืออินเทอร์เฟซที่กําหนดวิธีสําหรับความล้มเหลวและวิธีที่ประสบความสําเร็จ เช่น จะมีการนํา callback1 มาใช้เป็นอินสแตนซ์ต่อไปนี้

private class InstallsCallback implements JsonBatchCallback<InstallsListResponse> {

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

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

หมายเหตุ: เซิร์ฟเวอร์อาจโทรออกตามลําดับใดก็ได้ ดังนั้นโปรดอย่าอาศัยการรับสายตามลําดับที่ระบุไว้ในคําขอ หากต้องการมั่นใจว่าจะมีการโทร 2 ครั้งตามลําดับที่กําหนด คุณจะส่งคําขอเดียวไม่ได้ แต่ให้ส่งคําขอแรกเองแล้วรอคําตอบก่อนจึงส่งคําขอที่ 2

ตัวอย่างคําขอแบบกลุ่ม

ตัวอย่างต่อไปนี้แสดงวิธีแสดงรายการแอปที่ติดตั้งทั้งหมดในอุปกรณ์ของผู้ใช้ที่กําหนด การเรียกครั้งแรกจะใช้เพื่อรับรหัสขององค์กรและของผู้ใช้ และต้องทํางานตามลําดับ เมื่อได้รับรหัสอุปกรณ์ทั้งหมดด้วย 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();
  }
}