Envía solicitudes por lotes

En este documento, se muestra cómo agrupar llamadas a la API para reducir la cantidad de conexiones que debe hacer tu cliente.

Este documento trata específicamente sobre cómo realizar una solicitud por lotes mediante la biblioteca cliente de Java. También hay un ejemplo básico disponible en el Biblioteca cliente de las APIs de Google para .NET El sistema por lotes para la API de Google Play EMM usa el mismo HTTP como el sistema de procesamiento por lotes OData.

Descripción general

Cada solicitud que tu cliente realiza a través de la API de EMM de Google Play genera una cierta cantidad de sobrecarga. La API de EMM de Google Play admite el procesamiento por lotes para permitir que tu cliente coloque varias llamadas a la API en una sola solicitud.

Estos son algunos ejemplos de situaciones en las que podrías querer usar el procesamiento por lotes:

  • Se acaba de inscribir un dominio y ahora tiene muchos datos para subir.
  • Un usuario realizó cambios en datos mientras tu aplicación estaba sin conexión, por lo que esta debe sincronizar una gran cantidad de datos locales con el servidor.

En estos casos, en lugar de enviar cada llamada por separado, puedes agruparlas en una sola solicitud. Incluso puedes agrupar solicitudes para varios usuarios o para varias APIs de Google.

Sin embargo, tienes un límite de 1, 000 llamadas en una sola solicitud por lotes. Si necesitas hacer más llamadas, usa varias solicitudes por lotes.

Detalles del lote

Una solicitud por lotes consta de varias llamadas a la API combinadas en una solicitud JSON-RPC. En esta sección, se describe la sintaxis de la solicitud por lotes en detalle. Se incluye un ejemplo.

Nota: Un conjunto de solicitudes n agrupadas se considera en tu límite de uso como solicitudes n, no como una sola. La solicitud por lotes se divide en un conjunto de solicitudes antes de procesarse.

Formato de una solicitud por lotes

La biblioteca cliente de Java contiene llamadas para crear solicitudes para cada llamada a la API de EMM de Google Play. Por ejemplo, para enumerar todas las apps instaladas en un dispositivo, usarías lo siguiente:

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

Existe una llamada batch() adicional que puede poner en cola varias solicitudes, como se muestra a continuación:

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();
Cuando se llama a batchRequest.execute(), todas las solicitudes en cola se envían a la vez al servidor como un array JSON. El servidor aplica los encabezados y parámetros de consulta de la solicitud externa (según corresponda) a cada parte y, luego, trata cada parte como si fuera una solicitud JSON separada.

Respuesta a una solicitud por lotes

El servidor ejecuta cada solicitud por separado y agrupa el resultado en una sola respuesta hecha de un único array. La biblioteca cliente divide esta respuesta en respuestas individuales, y cada una se envía a la función de devolución de llamada que se pasa a queue(). La devolución de llamada es una interfaz que define un método para la falla y un método para el éxito. Por ejemplo, callback1 se implementaría como una instancia de lo siguiente:

private class InstallsCallback implements JsonBatchCallback<InstallsListResponse> {

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

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

Nota: Es posible que el servidor realice las llamadas en cualquier orden. Por lo tanto, no confíes en recibir los resultados en el orden especificado en tu solicitud. Si deseas asegurarte de que dos llamadas se produzcan en un orden determinado, no puedes enviarlas en una sola solicitud. envía la primera solicitud sola y espera una respuesta antes de enviar la segunda.

Ejemplo de solicitud por lotes

En el siguiente ejemplo, se muestra cómo obtener una lista de todas las apps instaladas en todos los dispositivos de un usuario determinado. Las primeras llamadas se utilizan para obtener el ID de la empresa y del usuario, y, en consecuencia, se deben ejecutar de forma secuencial. Una vez que se obtuvieron todos los IDs de dispositivo con enterprise.devices().list(), podemos realizar una solicitud por lotes para recuperar todas las aplicaciones en todos los dispositivos del usuario a la vez.

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