【发布时间】:2014-07-29 04:20:23
【问题描述】:
我正在使用改造构建一个应用程序。一切正常,但我担心我的 API 请求的大小,并希望使用分页将它们分开。
使用 Retrofit 自动浏览 API 以默认下载所有可用数据的最佳策略是什么?
【问题讨论】:
标签: android api-design retrofit okhttp
我正在使用改造构建一个应用程序。一切正常,但我担心我的 API 请求的大小,并希望使用分页将它们分开。
使用 Retrofit 自动浏览 API 以默认下载所有可用数据的最佳策略是什么?
【问题讨论】:
标签: android api-design retrofit okhttp
首先,您使用的后端服务需要支持分页。其次,如果您希望获得一个示例,说明如何使用改造从客户端实现这一点,我建议您查看@JakeWharton 的u2020 项目。 GalleryService 改造接口以非常简单的方式实现了这种机制。这是接口本身的link。
这是一个基于 u2020 项目的轻量级示例
// See how it uses a pagination index.
public interface GalleryService {
@GET("/gallery/{page}") //
Gallery listGallery(@Path("page") int page);
}
通过跟踪已从您的 REST 服务下载的项目总量以及每页预定义的最大项目数,您可以计算调用您的 REST 服务以下载下一组项目所需的页面索引。
然后你可以像这样调用你的rest api。
int nextPage = totalItemsAlreadyDownloaded / ITEMS_PER_PAGE + 1;
restApi.listGallery(nextPage);
这是一个基于 u2020 项目的非常简单的示例,但希望它能让您了解如何攻击它。
【讨论】:
所以我最终这样解决了我的问题:
我在我的服务器上使用 Grape,所以我安装了 Grape-kaminari gem 来处理服务器端的分页。 Grape-kaminari 在您的 url 上提供页面查询,并在标题响应中添加方便的分页信息。
我写了一个小类,让我可以自动递归浏览页面,直到我消耗完 API 上的所有数据:
package com.farmgeek.agricountantdemo.app.helpers;
import android.util.Log;
import retrofit.client.Header;
import retrofit.client.Response;
public class APIHelper {
public static PaginationData getPaginationData(Response response) {
int currentPage = 1;
int totalPages = 1;
for (Header header : response.getHeaders()) {
try {
if (header.getName().equals("X-Page")) {
currentPage = Integer.parseInt(header.getValue());
} else if (header.getName().equals("X-Total-Pages")) {
totalPages = Integer.parseInt(header.getValue());
}
} catch (NullPointerException e) {
// We don't care about header items
// with empty names, so just skip over
// them.
Log.w("APIHelper -> getPaginationData", "Skipped over header: " + e.getLocalizedMessage());
}
}
return new PaginationData(currentPage, totalPages);
}
public static class PaginationData {
public final int page;
public final int total;
public PaginationData(int currentPage, int totalPages) {
this.page = currentPage;
this.total = totalPages;
}
}
}
然后我会像这样在我的 API 调用中使用它:
public void getStuff(int page) {
final RestAdapter restAdapter = buildRestAdapter();
// Tell the sync adapter something's been added to the queue
ApiService apiService = restAdapter.create(ApiService.class);
apiService.getStuff(page, new Callback<List<Stuff>>() {
@Override
public void success(final List<Stuff> stuffList, Response response) {
final APIHelper.PaginationData pagination = APIHelper.getPaginationData(response);
for (final Stuff stuff : stuffList) {
handleRecord(stuff);
}
if (pagination.page == pagination.total) {
App.getEventBus().postSticky(new StuffSyncEvent());
App.getEventBus().post(new SuccessfulSyncEvent(Stuff.class));
} else {
// Otherwise pull down the next page
new StuffSyncRequestAdapter().getStuff(pagination.page+1);
}
}
@Override
public void failure(RetrofitError error) {
String errorMessage = error.getCause().getMessage();
App.getEventBus().post(new UnsuccessfulSyncEvent(Stuff.class, errorMessage));
}
});
}
【讨论】: