पेज किया गया डेटा लोड करें और दिखाएं

पेजिंग लाइब्रेरी से, पेज को लोड करने और दिखाने के लिए बेहतरीन सुविधाएं मिलती हैं बड़े डेटासेट से मिलाया गया डेटा. यह गाइड पेजिंग को इस्तेमाल करने का तरीका बताती है इस लाइब्रेरी का इस्तेमाल करके, नेटवर्क डेटा सोर्स से पेज किए गए डेटा की स्ट्रीम सेट अप की जा सकती है. RecyclerView में दिखेगा.

डेटा सोर्स तय करें

पहला कदम यह PagingSource लागू करना का इस्तेमाल करें. PagingSource एपीआई क्लास में load() तरीका है, जिसे आप संबंधित डेटा सोर्स को ऐक्सेस कर सकते हैं.

एक साथ काम नहीं करने वाली प्रोसेस के लिए Kotlin कोरूटीन इस्तेमाल करने के लिए, सीधे तौर पर PagingSource क्लास का इस्तेमाल करें लोड हो रहा है. पेजिंग लाइब्रेरी से एक साथ काम करने वाले दूसरे ऐप्लिकेशन के काम करने के लिए भी क्लास की सुविधा मिलती है फ़्रेमवर्क:

  • RxJava का इस्तेमाल करने के लिए, लागू करें RxPagingSource आज़माएं.
  • Guava से ListenableFuture का इस्तेमाल करने के लिए, लागू करें ListenableFuturePagingSource आज़माएं.

कुंजी और वैल्यू के टाइप चुनें

PagingSource<Key, Value> में दो तरह के पैरामीटर होते हैं: Key और Value. कुंजी डेटा लोड करने के लिए इस्तेमाल किए जाने वाले आइडेंटिफ़ायर के बारे में बताता है. साथ ही, वैल्यू शामिल हैं. उदाहरण के लिए, अगर नेटवर्क से User ऑब्जेक्ट के पेज लोड किए जाते हैं Int पेज संख्या को रेट्रोफ़िट, Key टाइप के तौर पर Int और Value टाइप के तौर पर User को चुनें.

पेजिंगसोर्स के बारे में बताएं

नीचे दिए गए उदाहरण में PagingSource जो लोड होता है पेज संख्या के हिसाब से आइटम के पेज. Key टाइप Int है और Value टाइप है User.

Kotlin

class ExamplePagingSource(
    val backend: ExampleBackendService,
    val query: String
) : PagingSource<Int, User>() {
  override suspend fun load(
    params: LoadParams<Int>
  ): LoadResult<Int, User> {
    try {
      // Start refresh at page 1 if undefined.
      val nextPageNumber = params.key ?: 1
      val response = backend.searchUsers(query, nextPageNumber)
      return LoadResult.Page(
        data = response.users,
        prevKey = null, // Only paging forward.
        nextKey = response.nextPageNumber
      )
    } catch (e: Exception) {
      // Handle errors in this block and return LoadResult.Error for
      // expected errors (such as a network failure).
    }
  }

  override fun getRefreshKey(state: PagingState<Int, User>): Int? {
    // Try to find the page key of the closest page to anchorPosition from
    // either the prevKey or the nextKey; you need to handle nullability
    // here.
    //  * prevKey == null -> anchorPage is the first page.
    //  * nextKey == null -> anchorPage is the last page.
    //  * both prevKey and nextKey are null -> anchorPage is the
    //    initial page, so return null.
    return state.anchorPosition?.let { anchorPosition ->
      val anchorPage = state.closestPageToPosition(anchorPosition)
      anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
    }
  }
}

Java

class ExamplePagingSource extends RxPagingSource<Integer, User> {
  @NonNull
  private ExampleBackendService mBackend;
  @NonNull
  private String mQuery;

  ExamplePagingSource(@NonNull ExampleBackendService backend,
    @NonNull String query) {
    mBackend = backend;
    mQuery = query;
  }

  @NotNull
  @Override
  public Single<LoadResult<Integer, User>> loadSingle(
    @NotNull LoadParams<Integer> params) {
    // Start refresh at page 1 if undefined.
    Integer nextPageNumber = params.getKey();
    if (nextPageNumber == null) {
      nextPageNumber = 1;
    }

    return mBackend.searchUsers(mQuery, nextPageNumber)
      .subscribeOn(Schedulers.io())
      .map(this::toLoadResult)
      .onErrorReturn(LoadResult.Error::new);
  }

  private LoadResult<Integer, User> toLoadResult(
    @NonNull SearchUserResponse response) {
    return new LoadResult.Page<>(
      response.getUsers(),
      null, // Only paging forward.
      response.getNextPageNumber(),
      LoadResult.Page.COUNT_UNDEFINED,
      LoadResult.Page.COUNT_UNDEFINED);
  }

  @Nullable
  @Override
  public Integer getRefreshKey(@NotNull PagingState<Integer, User> state) {
    // Try to find the page key of the closest page to anchorPosition from
    // either the prevKey or the nextKey; you need to handle nullability
    // here.
    //  * prevKey == null -> anchorPage is the first page.
    //  * nextKey == null -> anchorPage is the last page.
    //  * both prevKey and nextKey are null -> anchorPage is the
    //    initial page, so return null.
    Integer anchorPosition = state.getAnchorPosition();
    if (anchorPosition == null) {
      return null;
    }

    LoadResult.Page<Integer, User> anchorPage = state.closestPageToPosition(anchorPosition);
    if (anchorPage == null) {
      return null;
    }

    Integer prevKey = anchorPage.getPrevKey();
    if (prevKey != null) {
      return prevKey + 1;
    }

    Integer nextKey = anchorPage.getNextKey();
    if (nextKey != null) {
      return nextKey - 1;
    }

    return null;
  }
}

Java

class ExamplePagingSource extends ListenableFuturePagingSource<Integer, User> {
  @NonNull
  private ExampleBackendService mBackend;
  @NonNull
  private String mQuery;
  @NonNull
  private Executor mBgExecutor;

  ExamplePagingSource(
    @NonNull ExampleBackendService backend,
    @NonNull String query, @NonNull Executor bgExecutor) {
    mBackend = backend;
    mQuery = query;
    mBgExecutor = bgExecutor;
  }

  @NotNull
  @Override
  public ListenableFuture<LoadResult<Integer, User>> loadFuture(@NotNull LoadParams<Integer> params) {
    // Start refresh at page 1 if undefined.
    Integer nextPageNumber = params.getKey();
    if (nextPageNumber == null) {
      nextPageNumber = 1;
    }

    ListenableFuture<LoadResult<Integer, User>> pageFuture =
      Futures.transform(mBackend.searchUsers(mQuery, nextPageNumber),
      this::toLoadResult, mBgExecutor);

    ListenableFuture<LoadResult<Integer, User>> partialLoadResultFuture =
      Futures.catching(pageFuture, HttpException.class,
      LoadResult.Error::new, mBgExecutor);

    return Futures.catching(partialLoadResultFuture,
      IOException.class, LoadResult.Error::new, mBgExecutor);
  }

  private LoadResult<Integer, User> toLoadResult(@NonNull SearchUserResponse response) {
    return new LoadResult.Page<>(response.getUsers(),
    null, // Only paging forward.
    response.getNextPageNumber(),
    LoadResult.Page.COUNT_UNDEFINED,
    LoadResult.Page.COUNT_UNDEFINED);
  }

  @Nullable
  @Override
  public Integer getRefreshKey(@NotNull PagingState<Integer, User> state) {
    // Try to find the page key of the closest page to anchorPosition from
    // either the prevKey or the nextKey; you need to handle nullability
    // here.
    //  * prevKey == null -> anchorPage is the first page.
    //  * nextKey == null -> anchorPage is the last page.
    //  * both prevKey and nextKey are null -> anchorPage is the
    //    initial page, so return null.
    Integer anchorPosition = state.getAnchorPosition();
    if (anchorPosition == null) {
      return null;
    }

    LoadResult.Page<Integer, User> anchorPage = state.closestPageToPosition(anchorPosition);
    if (anchorPage == null) {
      return null;
    }

    Integer prevKey = anchorPage.getPrevKey();
    if (prevKey != null) {
      return prevKey + 1;
    }

    Integer nextKey = anchorPage.getNextKey();
    if (nextKey != null) {
      return nextKey - 1;
    }

    return null;
  }
}

आम तौर पर लागू किया जाने वाला PagingSource, इसमें दिए गए पैरामीटर में पास होता है कंस्ट्रक्टर को load() तरीके से जोड़ना होगा, ताकि किसी क्वेरी के लिए सही डेटा लोड किया जा सके. इस उदाहरण के लिए, वे पैरामीटर हैं:

  • backend: बैकएंड सेवा का इंस्टेंस, जो डेटा उपलब्ध कराता है
  • query: backend द्वारा बताई गई सेवा को भेजने के लिए खोज क्वेरी

LoadParams ऑब्जेक्ट में, लोड करने की कार्रवाई के बारे में जानकारी होती है. यह इसमें लोड की जाने वाली कुंजी और लोड किए जाने वाले आइटम की संख्या शामिल होती है.

LoadResult ऑब्जेक्ट में लोड कार्रवाई का नतीजा शामिल होता है. LoadResult एक ऐसी क्लास है जिसे सील किया गया है जो इस बात पर निर्भर करता है कि load() कॉल सफल हुई या नहीं, इन दोनों में से एक फ़ॉर्म होगा:

  • अगर लोड हो जाता है, तो LoadResult.Page ऑब्जेक्ट दिखाएं.
  • अगर लोड नहीं होता है, तो LoadResult.Error ऑब्जेक्ट दिखाएं.

यह डायग्राम दिखाता है कि इस उदाहरण में load() कैसे काम करता है हर लोड के लिए कुंजी मिलती है और बाद में होने वाले लोड के लिए कुंजी उपलब्ध कराती है.

हर एक load() कॉल पर, ExamplePagingSource मौजूदा कुंजी को लेता है
    और लोड करने के लिए अगली कुंजी लौटाता है.
पहली इमेज. डायग्राम में दिखाया गया है कि load(), कुंजी का इस्तेमाल और उसे अपडेट कैसे करता है.

PagingSource लागू करने पर, getRefreshKey() तरीका है जो PagingState ऑब्जेक्ट, पैरामीटर. जब डेटाload() शुरुआती लोड के बाद रीफ़्रेश या अमान्य हो जाता है. पेजिंग लाइब्रेरी इसे नया तरीका अपने-आप अपडेट हो जाता है.

गड़बड़ियां ठीक करना

कई वजहों से, डेटा लोड करने के अनुरोध पूरे नहीं हो पाते हैं. खास तौर पर, ऐसा तब होता है, जब एक नेटवर्क पर सेव किया जाता है. लोड करने के दौरान हुई गड़बड़ियों की रिपोर्ट दें. load() तरीके से LoadResult.Error ऑब्जेक्ट.

उदाहरण के लिए, ExamplePagingSource में कॉन्टेंट लोड होने में आने वाली गड़बड़ियों का पता लगाया जा सकता है और उनकी शिकायत की जा सकती है पिछले उदाहरण में, नीचे दिए गए यूआरएल को load() तरीके में जोड़कर देखें:

Kotlin

catch (e: IOException) {
  // IOException for network failures.
  return LoadResult.Error(e)
} catch (e: HttpException) {
  // HttpException for any non-2xx HTTP status codes.
  return LoadResult.Error(e)
}

Java

return backend.searchUsers(searchTerm, nextPageNumber)
  .subscribeOn(Schedulers.io())
  .map(this::toLoadResult)
  .onErrorReturn(LoadResult.Error::new);

Java

ListenableFuture<LoadResult<Integer, User>> pageFuture = Futures.transform(
  backend.searchUsers(query, nextPageNumber), this::toLoadResult,
  bgExecutor);

ListenableFuture<LoadResult<Integer, User>> partialLoadResultFuture = Futures.catching(
  pageFuture, HttpException.class, LoadResult.Error::new,
  bgExecutor);

return Futures.catching(partialLoadResultFuture,
  IOException.class, LoadResult.Error::new, bgExecutor);

Retrofit की गड़बड़ियों को मैनेज करने के बारे में ज़्यादा जानकारी के लिए, PagingSource एपीआई का रेफ़रंस.

PagingSource, यूज़र इंटरफ़ेस (यूआई) में LoadResult.Error ऑब्जेक्ट इकट्ठा करके उन्हें डिलीवर करता है आप इन पर कार्रवाई कर सकें. पेज लोड होने की स्थिति दिखाने के बारे में ज़्यादा जानकारी पाने के लिए यूज़र इंटरफ़ेस (यूआई) में, लोडिंग को मैनेज करें और प्रज़ेंट करें राज्य.

PagingData की स्ट्रीम सेट अप करें

इसके बाद, आपको PagingSource लागू करने के तरीके से पेज किए गए डेटा की स्ट्रीम की ज़रूरत होगी. अपने ViewModel में डेटा स्ट्रीम सेट अप करें. कॉन्टेंट बनाने Pager क्लास ऐसे तरीके उपलब्ध कराती है लेकिन आपकी खबरों की PagingData ऑब्जेक्ट से PagingSource. पेजिंग लाइब्रेरी, कई स्ट्रीम टाइप के साथ काम करती है. इसमें Flow, LiveData, और इसके Flowable और Observable टाइप शामिल हैं RxJava.

जब आप अपनी प्रतिक्रियाशील स्ट्रीम सेट अप करने के लिए Pager इंस्टेंस बनाते हैं, तो आपको ये काम ज़रूर करने चाहिए इस मामले में PagingConfig कॉन्फ़िगरेशन ऑब्जेक्ट और एक फ़ंक्शन है, जो Pager को बताता है कि PagingSource लागू करना:

Kotlin

val flow = Pager(
  // Configure how data is loaded by passing additional properties to
  // PagingConfig, such as prefetchDistance.
  PagingConfig(pageSize = 20)
) {
  ExamplePagingSource(backend, query)
}.flow
  .cachedIn(viewModelScope)

Java

// CoroutineScope helper provided by the lifecycle-viewmodel-ktx artifact.
CoroutineScope viewModelScope = ViewModelKt.getViewModelScope(viewModel);
Pager<Integer, User> pager = Pager<>(
  new PagingConfig(/* pageSize = */ 20),
  () -> ExamplePagingSource(backend, query));

Flowable<PagingData<User>> flowable = PagingRx.getFlowable(pager);
PagingRx.cachedIn(flowable, viewModelScope);

Java

// CoroutineScope helper provided by the lifecycle-viewmodel-ktx artifact.
CoroutineScope viewModelScope = ViewModelKt.getViewModelScope(viewModel);
Pager<Integer, User> pager = Pager<>(
  new PagingConfig(/* pageSize = */ 20),
  () -> ExamplePagingSource(backend, query));

PagingLiveData.cachedIn(PagingLiveData.getLiveData(pager), viewModelScope);

cachedIn() ऑपरेटर, डेटा स्ट्रीम को शेयर करता है और लोड किए गए डेटा को कैश मेमोरी में सेव करता है दिए गए CoroutineScope के साथ डेटा. इस उदाहरण में, viewModelScope का इस्तेमाल किया गया है लाइफ़साइकल lifecycle-viewmodel-ktx आर्टफ़ैक्ट से मिला है.

Pager ऑब्जेक्ट, PagingSource ऑब्जेक्ट से load() तरीके को कॉल करता है, उसे LoadParams ऑब्जेक्ट और उन्हें LoadResult ऑब्जेक्ट वापस किया जा सकता है.

RecyclerView अडैप्टर तय करना

RecyclerView में डेटा पाने के लिए, आपको अडैप्टर भी सेट अप करना होगा सूची. पेजिंग लाइब्रेरी इसके लिए PagingDataAdapter क्लास उपलब्ध कराती है उद्देश्य है.

PagingDataAdapter का दायरा बढ़ाने वाली क्लास तय करें. उदाहरण में, UserAdapter, RecyclerView देने के लिए PagingDataAdapter की अवधि बढ़ा देता है User टाइप के सूची आइटम और UserViewHolder को व्यू के तौर पर इस्तेमाल करने के लिए अडैप्टर मालिक:

Kotlin

class UserAdapter(diffCallback: DiffUtil.ItemCallback<User>) :
  PagingDataAdapter<User, UserViewHolder>(diffCallback) {
  override fun onCreateViewHolder(
    parent: ViewGroup,
    viewType: Int
  ): UserViewHolder {
    return UserViewHolder(parent)
  }

  override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
    val item = getItem(position)
    // Note that item can be null. ViewHolder must support binding a
    // null item as a placeholder.
    holder.bind(item)
  }
}

Java

class UserAdapter extends PagingDataAdapter<User, UserViewHolder> {
  UserAdapter(@NotNull DiffUtil.ItemCallback<User> diffCallback) {
    super(diffCallback);
  }

  @NonNull
  @Override
  public UserViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    return new UserViewHolder(parent);
  }

  @Override
  public void onBindViewHolder(@NonNull UserViewHolder holder, int position) {
    User item = getItem(position);
    // Note that item can be null. ViewHolder must support binding a
    // null item as a placeholder.
    holder.bind(item);
  }
}

Java

class UserAdapter extends PagingDataAdapter<User, UserViewHolder> {
  UserAdapter(@NotNull DiffUtil.ItemCallback<User> diffCallback) {
    super(diffCallback);
  }

  @NonNull
  @Override
  public UserViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    return new UserViewHolder(parent);
  }

  @Override
  public void onBindViewHolder(@NonNull UserViewHolder holder, int position) {
    User item = getItem(position);
    // Note that item can be null. ViewHolder must support binding a
    // null item as a placeholder.
    holder.bind(item);
  }
}

आपके अडैप्टर को onCreateViewHolder() और onBindViewHolder() तरीकों का इस्तेमाल कर सकते हैं और DiffUtil.ItemCallback. यह उसी तरह से काम करता है, जैसा कि आम तौर पर RecyclerView सूची तय करते समय किया जाता है अडैप्टर:

Kotlin

object UserComparator : DiffUtil.ItemCallback<User>() {
  override fun areItemsTheSame(oldItem: User, newItem: User): Boolean {
    // Id is unique.
    return oldItem.id == newItem.id
  }

  override fun areContentsTheSame(oldItem: User, newItem: User): Boolean {
    return oldItem == newItem
  }
}

Java

class UserComparator extends DiffUtil.ItemCallback<User> {
  @Override
  public boolean areItemsTheSame(@NonNull User oldItem,
    @NonNull User newItem) {
    // Id is unique.
    return oldItem.id.equals(newItem.id);
  }

  @Override
  public boolean areContentsTheSame(@NonNull User oldItem,
    @NonNull User newItem) {
    return oldItem.equals(newItem);
  }
}

Java

class UserComparator extends DiffUtil.ItemCallback<User> {
  @Override
  public boolean areItemsTheSame(@NonNull User oldItem,
    @NonNull User newItem) {
    // Id is unique.
    return oldItem.id.equals(newItem.id);
  }

  @Override
  public boolean areContentsTheSame(@NonNull User oldItem,
    @NonNull User newItem) {
    return oldItem.equals(newItem);
  }
}

अपने यूज़र इंटरफ़ेस (यूआई) में पेज डेटा दिखाएं

आपने PagingSource तय कर लिया है. इसलिए, अब से आपके ऐप्लिकेशन के लिए PagingData की एक स्ट्रीम जनरेट करें और PagingDataAdapter तय करें, तो इन एलिमेंट को एक साथ कनेक्ट करने और पेज किया गया डेटा गतिविधि.

अपनी गतिविधि के onCreate या फ़्रैगमेंट में ये चरण पूरे करें onViewCreated तरीका:

  1. अपनी PagingDataAdapter क्लास का इंस्टेंस बनाएं.
  2. PagingDataAdapter इंस्टेंस को RecyclerView शामिल करें, जिसमें आप पेज डेटा वाला डेटा दिखाना चाहते हैं.
  3. PagingData स्ट्रीम को देखें और जनरेट की गई हर वैल्यू को अडैप्टर की submitData() तरीके से पुष्टि करता है.

Kotlin

val viewModel by viewModels<ExampleViewModel>()

val pagingAdapter = UserAdapter(UserComparator)
val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
recyclerView.adapter = pagingAdapter

// Activities can use lifecycleScope directly; fragments use
// viewLifecycleOwner.lifecycleScope.
lifecycleScope.launch {
  viewModel.flow.collectLatest { pagingData ->
    pagingAdapter.submitData(pagingData)
  }
}

Java

ExampleViewModel viewModel = new ViewModelProvider(this)
  .get(ExampleViewModel.class);

UserAdapter pagingAdapter = new UserAdapter(new UserComparator());
RecyclerView recyclerView = findViewById<RecyclerView>(
  R.id.recycler_view);
recyclerView.adapter = pagingAdapter

viewModel.flowable
  // Using AutoDispose to handle subscription lifecycle.
  // See: https://github.com/uber/AutoDispose.
  .to(autoDisposable(AndroidLifecycleScopeProvider.from(this)))
  .subscribe(pagingData -> pagingAdapter.submitData(lifecycle, pagingData));

Java

ExampleViewModel viewModel = new ViewModelProvider(this)
  .get(ExampleViewModel.class);

UserAdapter pagingAdapter = new UserAdapter(new UserComparator());
RecyclerView recyclerView = findViewById<RecyclerView>(
  R.id.recycler_view);
recyclerView.adapter = pagingAdapter

// Activities can use getLifecycle() directly; fragments use
// getViewLifecycleOwner().getLifecycle().
viewModel.liveData.observe(this, pagingData ->
  pagingAdapter.submitData(getLifecycle(), pagingData));

RecyclerView सूची में अब डेटा सोर्स का पेज किया गया डेटा दिखता है और ज़रूरत पड़ने पर अपने-आप कोई दूसरा पेज लोड करता है.

अन्य संसाधन

पेजिंग लाइब्रेरी के बारे में ज़्यादा जानने के लिए, इन अतिरिक्त संसाधनों को देखें:

कोड लैब

सैंपल