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

Paging Library多次搜索查询后停止加载数据问题求助

Fixing Paging Library Data Loading Stops After Multiple Search Queries

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 14:52:36