Paging Library多次搜索查询后停止加载数据问题求助
Hey there, let's break down why your GitHub repo pagination stops working after multiple search queries, and how to fix it. I've gone through your code and spotted a couple of critical issues that are almost certainly causing this behavior.
The Root Cause
The biggest problem here is that you're using synchronous network calls with execute() inside your RepoDataSource methods. This blocks the background thread that the Paging Library uses to load data. When you run multiple search queries, these blocked threads can pile up, eventually grinding your data loading to a halt. Debug mode might mask this because thread scheduling behaves differently in debug environments.
Another minor but important gap: you're not handling failed/empty responses properly, which can leave the Paging Library in an inconsistent state where it stops trying to load more data.
Step-by-Step Fixes
1. Replace Synchronous Network Calls with Asynchronous Ones
Modify your RepoDataSource to use OkHttp's asynchronous enqueue() instead of execute(). This keeps the Paging Library's background threads free to handle new requests.
public class RepoDataSource extends PageKeyedDataSource<Integer, Repo> { private final GithubService githubService; private final String query; private Call<RepoSearchResponse> currentCall; // Track active calls to cancel if needed public RepoDataSource(GithubService githubService, String query) { this.githubService = githubService; this.query = query; } @Override public void loadInitial(@NonNull LoadInitialParams<Integer> params, @NonNull LoadInitialCallback<Integer, Repo> callback) { Timber.d("Initial load for query: %s", query); // Cancel any existing call before starting a new one if (currentCall != null) currentCall.cancel(); currentCall = githubService.searchRepos(query, 1); currentCall.enqueue(new Callback<RepoSearchResponse>() { @Override public void onResponse(@NonNull Call<RepoSearchResponse> call, @NonNull Response<RepoSearchResponse> response) { if (response.isSuccessful() && response.body() != null) { List<Repo> items = response.body().getItems(); callback.onResult(items, 1, 2); } else { // Handle empty or failed response gracefully callback.onResult(Collections.emptyList(), null, null); } currentCall = null; } @Override public void onFailure(@NonNull Call<RepoSearchResponse> call, @NonNull Throwable t) { Timber.e(t, "Failed to load initial repos"); currentCall = null; // Optionally notify the UI of the error here } }); } @Override public void loadBefore(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, Repo> callback) { // No implementation needed for forward-only pagination } @Override public void loadAfter(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, Repo> callback) { Timber.d("Fetching page %s for query: %s", params.key, query); if (currentCall != null) currentCall.cancel(); currentCall = githubService.searchRepos(query, params.key); currentCall.enqueue(new Callback<RepoSearchResponse>() { @Override public void onResponse(@NonNull Call<RepoSearchResponse> call, @NonNull Response<RepoSearchResponse> response) { if (response.isSuccessful() && response.body() != null) { List<Repo> items = response.body().getItems(); callback.onResult(items, params.key + 1); } else { callback.onResult(Collections.emptyList(), null); } currentCall = null; } @Override public void onFailure(@NonNull Call<RepoSearchResponse> call, @NonNull Throwable t) { Timber.e(t, "Failed to load page %d", params.key); currentCall = null; // Optionally add retry logic here } }); } @Override public void invalidate() { super.invalidate(); // Cancel active call when data source is invalidated (e.g., new search query) if (currentCall != null) currentCall.cancel(); } }
2. Add Empty Query Handling in ViewModel
Prevent unnecessary network calls and inconsistent states when the user submits an empty query:
public LiveData<PagedList<Repo>> getRepos() { return Transformations.switchMap(searchQuery, query -> { if (query == null || query.trim().isEmpty()) { // Return an empty paged list for empty queries return new LivePagedListBuilder<>(new DataSource.Factory<Integer, Repo>() { @NonNull @Override public DataSource<Integer, Repo> create() { return new PageKeyedDataSource<Integer, Repo>() { @Override public void loadInitial(@NonNull LoadInitialParams<Integer> params, @NonNull LoadInitialCallback<Integer, Repo> callback) { callback.onResult(Collections.emptyList(), null, null); } @Override public void loadBefore(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, Repo> callback) {} @Override public void loadAfter(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, Repo> callback) {} }; } }, pagedListConfig).build(); } RepoDataSourceFactory factory = repository.getRepoPagedFactory(query); return new LivePagedListBuilder<>(factory, pagedListConfig).build(); }); }
3. Validate Your PagedListConfig
Make sure your pagination config is set up for reliable behavior:
// Initialize this in your ViewModel constructor private PagedList.Config pagedListConfig = new PagedList.Config.Builder() .setPageSize(30) // Match GitHub API's default page size .setEnablePlaceholders(false) // Avoid UI glitches from empty placeholders .setPrefetchDistance(15) // Start loading next page when 15 items are left .build();
Why This Works
- Asynchronous calls keep the Paging Library's background threads unblocked, so it can handle new search queries without getting stuck.
- Canceling active calls when a new query comes in prevents unnecessary network traffic and frees up resources.
- Graceful error handling ensures the Paging Library doesn't get stuck in a broken state if a request fails.
内容的提问来源于stack exchange,提问作者rost

