Batchanfragen senden

In diesem Dokument wird beschrieben, wie API-Aufrufe in einem Batch zusammengefasst werden, um die Anzahl der Verbindungen zu verringern, die der Client herstellen muss.

In diesem Dokument geht es speziell um das Erstellen einer Batchanfrage mit der Java-Client-Bibliothek. Ein einfaches Beispiel finden Sie auch in der Google API-Clientbibliothek für .NET Das Batchsystem für die Google Play EMM API verwendet dasselbe HTTP- wie das Batchverarbeitungssystem von OData.

Übersicht

Jede Anfrage, die Ihr Client über die Google Play EMM API stellt, führt zu einem bestimmten Mehraufwand. Die Google Play EMM API unterstützt die Batchverarbeitung, damit Ihr Client mehrere API-Aufrufe in einer Anfrage zusammenfassen kann.

Im Folgenden finden Sie einige Beispiele für Situationen, in denen Sie Batches verwenden können:

  • Gerade wurde eine Domain registriert, auf der viele Daten hochgeladen werden können.
  • Ein Nutzer hat Änderungen an Daten vorgenommen, während Ihre Anwendung offline war. Daher muss Ihre Anwendung viele lokale Daten mit dem Server synchronisieren.

In solchen Fällen können Sie, anstatt jeden Aufruf einzeln zu senden, die Aufrufe in einer einzigen Anfrage zusammenfassen. Sie haben sogar die Möglichkeit, Anfragen für mehrere Nutzer oder Google APIs zu gruppieren.

Eine Batchanfrage ist jedoch auf 1.000 Aufrufe beschränkt. Wenn Sie mehr Aufrufe ausführen müssen, verwenden Sie mehrere Batchanfragen.

Batchdetails

Eine Batchanfrage besteht aus mehreren API-Aufrufen, die zu einer JSON-RPC-Anfrage zusammengefasst sind. In diesem Abschnitt wird die Syntax von Batchanfragen ausführlich beschrieben. Weiter unten finden Sie ein Beispiel.

Hinweis: Wenn n Anfragen zu einem Batch zusammengefasst sind, werden auch n Anfragen auf Ihr Nutzungskontingent angerechnet, nicht nur eine einzige Anfrage. Die Batchanfrage wird vor der Verarbeitung in eine Reihe von Anfragen aufgeteilt.

Format einer Batchanfrage

Die Java-Clientbibliothek enthält Aufrufe zum Erstellen von Anfragen für jeden Aufruf der Google Play EMM API. Um beispielsweise alle auf einem Gerät installierten Apps aufzulisten, würden Sie Folgendes verwenden:

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

Es gibt einen zusätzlichen batch()-Aufruf, der mehrere Anfragen in die Warteschlange stellen kann:

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();
Wenn batchRequest.execute() aufgerufen wird, werden alle in der Warteschlange gestellten Anfragen gleichzeitig als JSON-Array an den Server gesendet. Der Server wendet die Suchparameter und Header der äußeren Anfrage (je nach Bedarf) auf jeden Teil an und behandelt dann jeden Teil so, als wäre er eine separate JSON-Anfrage.

Antwort auf eine Batchanfrage

Der Server führt jede einzelne Anfrage aus und gruppiert das Ergebnis in eine einzige Antwort aus einem einzelnen Array. Die Clientbibliothek teilt diese Antwort in einzelne Antworten auf und jede einzelne wird an die Callback-Funktion gesendet, die an queue() übergeben wurde. Der Callback ist eine Schnittstelle, die eine Methode für den Fehler und eine für den Erfolg definiert. Beispielsweise wird callback1 als Instanz von Folgendem implementiert:

private class InstallsCallback implements JsonBatchCallback<InstallsListResponse> {

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

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

Hinweis: Der Server kann die Aufrufe in beliebiger Reihenfolge ausführen. Verlassen Sie sich also nicht darauf, Ergebnisse in der in der Anfrage angegebenen Reihenfolge zu erhalten. Wenn Sie sicherstellen möchten, dass zwei Aufrufe in einer bestimmten Reihenfolge erfolgen, können Sie sie nicht in einer einzelnen Anfrage senden. senden Sie stattdessen die erste Anfrage und warten Sie auf eine Antwort, bevor Sie die zweite senden.

Beispiel-Batchanfrage

Das folgende Beispiel zeigt, wie alle Apps aufgelistet werden, die auf allen Geräten eines bestimmten Nutzers installiert sind. Die ersten Aufrufe werden verwendet, um die ID des Unternehmens und des Nutzers zu erhalten, und müssen entsprechend der Reihe nach ausgeführt werden. Sobald alle Geräte-IDs mit enterprise.devices().list() abgerufen wurden, können wir eine Batchanfrage ausführen, um alle Apps auf allen Geräten des Nutzers gleichzeitig abzurufen.

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