Batchanfragen senden

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

In diesem Dokument wird ausschließlich das Erstellen einer Batchanfrage mit der Java-Clientbibliothek beschrieben. Ein einfaches Beispiel ist auch in der Google API-Clientbibliothek für .NET verfügbar. Das Batchsystem für die Google Play EMM API verwendet dieselbe HTTP-Syntax wie das Batchverarbeitungssystem von OData.

Übersicht

Jede Anfrage, die Ihr Client über die Google Play EMM API sendet, führt zu einem gewissen Overhead. Die Google Play EMM API unterstützt Stapelanfragen, damit der Client mehrere API-Aufrufe in einer einzigen Anfrage zusammenfassen kann.

Hier sind einige Beispiele für Situationen, in denen die Batchverarbeitung sinnvoll sein kann:

  • Eine Domain wurde gerade registriert und hat jetzt viele Daten hochzuladen.
  • Ein Nutzer hat Daten geändert, während Ihre Anwendung offline war, sodass Ihre Anwendung viele lokale Daten mit dem Server synchronisieren muss.

In solchen Fällen können Sie, anstatt jeden Aufruf separat zu senden, eine einzelne Anfrage zusammenfassen. Sie können sogar Anfragen für mehrere Nutzer oder für mehrere Google APIs gruppieren.

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

Batchdetails

Eine Batchanfrage besteht aus mehreren API-Aufrufen, die in einer JSON-RPC-Anfrage kombiniert werden. In diesem Abschnitt wird die Syntax von Batchanfragen im Detail beschrieben. Ein Beispiel finden Sie im folgenden Abschnitt.

Hinweis: Eine Gruppe von n Anfragen in einem Stapel wird auf Ihr Nutzungskontingent angerechnet und ist n Anfragen. 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 Google Play EMM API-Aufruf. Wenn Sie beispielsweise alle auf einem Gerät installierten Apps auflisten möchten, verwenden Sie Folgendes:

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 enthaltenen 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 als separate JSON-Anfrage.

Antwort auf eine Batchanfrage

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

private class InstallsCallback implements JsonBatchCallback<InstallsListResponse> {

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

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

Hinweis: Ihre Aufrufe können vom Server in beliebiger Reihenfolge ausgeführt werden. Daher sollten Sie sich nicht darauf verlassen, dass Sie Ergebnisse in der in Ihrer Anfrage angegebenen Reihenfolge erhalten. Wenn Sie sichergehen 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 allein und warten Sie auf eine Antwort, bevor Sie die zweite senden.

Beispiel-Batchanfrage

Das folgende Beispiel zeigt, wie Sie alle auf allen Nutzergeräten installierten Apps auflisten. Die ersten Aufrufe werden verwendet, um die ID des Unternehmens und des Nutzers abzurufen. Daher müssen sie nacheinander ausgeführt werden. Sobald alle Geräte-IDs mit enterprise.devices().list() abgerufen wurden, können wir eine Batchanfrage senden, um alle Anwendungen auf allen Nutzergeräten 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();
  }
}