You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Retrofit CallAdapter改造需求:分页接口多页与全量数据获取

Alright, let's extend your existing PaginatedCall CallAdapter to support fetching subsequent pages and grabbing all results in one go. Here's a step-by-step approach tailored to your setup:


1. Update the PaginatedCall Interface

First, we need to add methods to handle next-page checks, fetching the next page, and bulk-fetching all pages. Modify your interface like this:

public interface PaginatedCall<T> {
    // Existing methods for first page
    List<T> execute() throws IOException;
    void enqueue(Callback<List<T>> callback);

    // New: Check if there's a next page available
    boolean hasNextPage();

    // New: Get a call instance for the next page
    PaginatedCall<T> nextPage();

    // New: Sync fetch all pages at once
    List<T> executeAll() throws IOException;

    // New: Async fetch all pages at once
    void enqueueAll(Callback<List<T>> callback);
}

2. Modify the CallAdapter Implementation

Assuming your API returns a paginated response with metadata (like currentPage, totalPages, or a hasNext flag), we'll update the adapter to track this state and build subsequent page requests.

First, let's define a sample paginated response model (adjust this to match your actual API response structure):

public class PaginatedResponse<T> {
    public List<T> data; // Your list of items
    public int currentPage;
    public int totalPages;
    // Or use a boolean `hasNext` if your API provides that instead
}

Now update the CallAdapter implementation:

public class PaginatedCallAdapter<T> implements CallAdapter<T, PaginatedCall<T>> {
    private final Type responseType;
    private final Retrofit retrofit;

    public PaginatedCallAdapter(Type responseType, Retrofit retrofit) {
        this.responseType = responseType;
        this.retrofit = retrofit;
    }

    @Override
    public Type responseType() {
        return responseType;
    }

    @Override
    public PaginatedCall<T> adapt(Call<T> call) {
        return new PaginatedCallImpl<>(call, retrofit);
    }

    private static class PaginatedCallImpl<T> implements PaginatedCall<T> {
        private final Call<T> originalCall;
        private final Retrofit retrofit;
        private PaginatedResponse<T> lastResponse;
        private int currentPage = 1;

        public PaginatedCallImpl(Call<T> originalCall, Retrofit retrofit) {
            this.originalCall = originalCall;
            this.retrofit = retrofit;
        }

        @Override
        public List<T> execute() throws IOException {
            Response<T> response = originalCall.execute();
            if (!response.isSuccessful()) {
                throw new IOException("HTTP error code: " + response.code());
            }
            lastResponse = (PaginatedResponse<T>) response.body();
            currentPage = lastResponse.currentPage;
            return lastResponse.data;
        }

        @Override
        public void enqueue(Callback<List<T>> callback) {
            originalCall.enqueue(new Callback<T>() {
                @Override
                public void onResponse(Call<T> call, Response<T> response) {
                    if (response.isSuccessful()) {
                        lastResponse = (PaginatedResponse<T>) response.body();
                        currentPage = lastResponse.currentPage;
                        callback.onResponse(lastResponse.data);
                    } else {
                        callback.onFailure(new IOException("HTTP error code: " + response.code()));
                    }
                }

                @Override
                public void onFailure(Call<T> call, Throwable t) {
                    callback.onFailure(t);
                }
            });
        }

        @Override
        public boolean hasNextPage() {
            return lastResponse != null && currentPage < lastResponse.totalPages;
            // If your API uses a `hasNext` flag instead, use:
            // return lastResponse != null && lastResponse.hasNext;
        }

        @Override
        public PaginatedCall<T> nextPage() {
            if (!hasNextPage()) {
                throw new IllegalStateException("No more pages available");
            }
            // Build the next page request by updating the `page` query parameter
            HttpUrl originalUrl = originalCall.request().url();
            HttpUrl newUrl = originalUrl.newBuilder()
                    .setQueryParameter("page", String.valueOf(currentPage + 1))
                    .build();
            Request newRequest = originalCall.request().newBuilder()
                    .url(newUrl)
                    .build();
            Call<T> nextCall = retrofit.newCall(newRequest);
            return new PaginatedCallImpl<>(nextCall, retrofit);
        }

        @Override
        public List<T> executeAll() throws IOException {
            List<T> allResults = new ArrayList<>();
            PaginatedCall<T> currentCall = this;
            do {
                List<T> pageResults = currentCall.execute();
                allResults.addAll(pageResults);
                currentCall = currentCall.hasNextPage() ? currentCall.nextPage() : null;
            } while (currentCall != null);
            return allResults;
        }

        @Override
        public void enqueueAll(Callback<List<T>> callback) {
            List<T> accumulatedResults = new ArrayList<>();
            fetchNextPage(accumulatedResults, callback);
        }

        private void fetchNextPage(List<T> accumulatedResults, Callback<List<T>> finalCallback) {
            this.enqueue(new Callback<List<T>>() {
                @Override
                public void onResponse(List<T> pageData) {
                    accumulatedResults.addAll(pageData);
                    if (hasNextPage()) {
                        nextPage().fetchNextPage(accumulatedResults, finalCallback);
                    } else {
                        finalCallback.onResponse(accumulatedResults);
                    }
                }

                @Override
                public void onFailure(Throwable t) {
                    finalCallback.onFailure(t);
                }
            });
        }
    }
}

3. Usage Examples

Fetch the Second Page

// Sync approach
PaginatedCall<Book> bookCall = getBooks();
List<Book> firstPage = bookCall.execute();

if (bookCall.hasNextPage()) {
    PaginatedCall<Book> secondPageCall = bookCall.nextPage();
    List<Book> secondPage = secondPageCall.execute();
    // Process second page data
}

// Async approach
bookCall.enqueue(new Callback<List<Book>>() {
    @Override
    public void onResponse(List<Book> firstPage) {
        if (bookCall.hasNextPage()) {
            bookCall.nextPage().enqueue(new Callback<List<Book>>() {
                @Override
                public void onResponse(List<Book> secondPage) {
                    // Process second page data
                }

                @Override
                public void onFailure(Throwable t) {
                    // Handle error
                }
            });
        }
    }

    @Override
    public void onFailure(Throwable t) {
        // Handle error
    }
});

Fetch All Pages at Once

// Sync approach
List<Book> allBooks = getBooks().executeAll();

// Async approach
getBooks().enqueueAll(new Callback<List<Book>>() {
    @Override
    public void onResponse(List<Book> allBooks) {
        // Process all books
    }

    @Override
    public void onFailure(Throwable t) {
        // Handle error
    }
});

Key Notes

  • API Compatibility: If your API uses Link Headers (like GitHub's API) instead of response-body metadata, adjust the hasNextPage and nextPage methods to parse the Link Header for the next page URL instead of manually updating the page parameter.
  • Cancellation: Add a void cancel() method to PaginatedCall and delegate it to the underlying Call instance to handle request cancellation (critical for avoiding memory leaks in Android).
  • Error Handling: Extend the callback interfaces if you need to track partial results when fetching all pages asynchronously.

内容的提问来源于stack exchange,提问作者Tom Insam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:24:48