本文档介绍了如何对 API 调用进行批处理以减少客户端必须建立的连接数。
本文档专门介绍了如何使用 Java 客户端库发出批量请求。 适用于 .NET 的 Google API 客户端库也提供了一个基本示例。Google Play EMM API 的批处理系统使用与 OData 批处理系统相同的 HTTP 语法。
概览
客户端通过 Google Play EMM API 发出的每个请求都会产生一定的开销。Google Play EMM API 支持批处理,这样您的客户端就可以将多个 API 调用放入单个请求中。
下面列举了一些您可能希望使用批处理的情况:
- 某个域名刚刚刚刚注册,现在包含大量数据要上传。
- 用户在应用离线时对数据进行了更改,因此应用需要将大量本地数据与服务器同步。
在这种情况下,您可以将这些调用组合成一个请求,而不是单独发送每个调用。您甚至可以对多个用户或多个 Google API 的请求进行分组。
不过,每个批量请求最多只能包含 1000 个调用。如果您需要发出超过此数量的调用,请使用多个批量请求。
批量详情
一个批量请求由合并为一个 JSON-RPC 请求的多个 API 调用组成。本部分详细介绍了批量请求语法,还在后面的部分中提供了示例。
注意:一组批量处理的 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) { ... } }
注意:服务器可能会以任何顺序执行调用,因此请勿以请求中指定的顺序接收结果。如果您想确保两个调用都按指定顺序进行,您不能在单个请求中发送它们;而是要单独发送第一个请求,并等待响应,然后再发送第二个请求。
批量请求示例
以下示例展示了如何列出指定的所有用户设备上安装的所有应用。首次调用用于获取企业和用户 ID,因此必须按顺序执行。使用 enterprise.devices().list()
获取所有设备 ID 后,我们就可以执行批量请求,一次性检索用户所有设备上的所有应用。
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(); } }