جمع‌آوری داده‌های صفحه‌بندی‌شده

این راهنما براساس نمای کلی کتابخانه صفحه‌بندی ساخته شده است و درباره نحوه سفارشی‌سازی راه‌حل بارگیری داده‌های برنامه برای برآورده کردن نیازهای معماری برنامه بحث می‌کند.

ساختن فهرست قابل‌مشاهده

معمولاً کد میانای کاربر شما یک شیء LiveData<PagedList> را (یا، اگر از RxJava2 استفاده می‌کنید، یک شیء Flowable<PagedList> یا Observable<PagedList> را) که در ViewModel برنامه‌تان قرار دارد، مشاهده می‌کند. این شیء قابل‌مشاهده اتصالی بین ارائه و محتوای داده‌های فهرست برنامه‌تان ایجاد می‌کند.

برای ایجاد یکی از این اشیاء قابل‌مشاهده PagedList، نمونه‌ای از DataSource.Factory را به شیء LivePagedListBuilder یا RxPagedListBuilder منتقل کنید. شیء DataSource صفحه‌ها را برای یک PagedList بار می‌کند. کلاس کارخانه نمونه‌های جدیدی از PagedList را در پاسخ به به‌روزرسانی‌های محتوا، مانند نامعتبر شدن جدول پایگاه داده و بازآوری‌های شبکه، ایجاد می‌کند. کتابخانه پایداری Room می‌تواند اشیاء DataSource.Factory را برای شما فراهم کند، یا می‌توانید خودتان بسازید.

تکه‌کد زیر نحوه ایجاد نمونه جدیدی از LiveData<PagedList> را در کلاس ViewModel برنامه‌تان بااستفاده از قابلیت‌های ساختن DataSource.Factory Room نشان می‌دهد:

ConcertDao

کاتلین

@Dao
interface ConcertDao {
    // The Int type parameter tells Room to use a PositionalDataSource
    // object, with position-based loading under the hood.
    @Query("SELECT * FROM concerts ORDER BY date DESC")
    fun concertsByDate(): DataSource.Factory<Int, Concert>
}

جاوا

@Dao
public interface ConcertDao {
    // The Integer type parameter tells Room to use a PositionalDataSource
    // object, with position-based loading under the hood.
    @Query("SELECT * FROM concerts ORDER BY date DESC")
    DataSource.Factory<Integer, Concert> concertsByDate();
}

ConcertViewModel

کاتلین

// The Int type argument corresponds to a PositionalDataSource object.
val myConcertDataSource : DataSource.Factory<Int, Concert> =
       concertDao.concertsByDate()

val concertList = myConcertDataSource.toLiveData(pageSize = 50)

جاوا

// The Integer type argument corresponds to a PositionalDataSource object.
DataSource.Factory<Integer, Concert> myConcertDataSource =
       concertDao.concertsByDate();

LiveData<PagedList<Concert>> concertList =
        LivePagedListBuilder(myConcertDataSource, /* page size */ 50).build();

پیکربندی صفحه‌بندی خودتان را تعریف کنید

برای پیکربندی بیشتر LiveData<PagedList> برای موارد پیشرفته، می‌توانید پیکربندی صفحه‌بندی خودتان را نیز تعریف کنید. به‌طور خاص، می‌توانید ویژگی‌های زیر را تعریف کنید:

  • اندازه صفحه: تعداد موارد در هر صفحه.
  • فاصله پیش‌واکشی: با توجه به آخرین مورد قابل‌مشاهده در میانای کاربر برنامه، تعداد موارد فراتر از این آخرین موردی که «کتابخانه صفحه‌بندی» باید تلاش کند ازقبل واکشی کند. این مقدار باید چند برابر بزرگ‌تر از اندازه صفحه باشد.
  • حضور جای‌بان: تعیین می‌کند که آیا میانای کاربری جای‌بان‌ها را برای موارد فهرستی که هنوز بارگیری‌شان تمام نشده است نمایش دهد یا نه. برای بحث درباره مزایا و معایب استفاده از جای‌بان‌ها، با نحوه ارائه جای‌بان‌ها در واسط کاربر آشنا شوید.

اگر می‌خواهید کنترل بیشتری بر زمان بار شدن فهرست از پایگاه داده برنامه‌تان توسط «کتابخانه صفحه‌بندی» داشته باشید، یک شیء Executor سفارشی را به LivePagedListBuilder ارسال کنید، همان‌طور که در قطعه کد زیر نشان داده شده است:

ConcertViewModel

کاتلین

val myPagingConfig = Config(
        pageSize = 50,
        prefetchDistance = 150,
        enablePlaceholders = true
)

// The Int type argument corresponds to a PositionalDataSource object.
val myConcertDataSource : DataSource.Factory<Int, Concert> =
        concertDao.concertsByDate()

val concertList = myConcertDataSource.toLiveData(
        pagingConfig = myPagingConfig,
        fetchExecutor = myExecutor
)

جاوا

PagedList.Config myPagingConfig = new PagedList.Config.Builder()
        .setPageSize(50)
        .setPrefetchDistance(150)
        .setEnablePlaceholders(true)
        .build();

// The Integer type argument corresponds to a PositionalDataSource object.
DataSource.Factory<Integer, Concert> myConcertDataSource =
        concertDao.concertsByDate();

LiveData<PagedList<Concert>> concertList =
        new LivePagedListBuilder<>(myConcertDataSource, myPagingConfig)
            .setFetchExecutor(myExecutor)
            .build();

نوع منبع داده درست را انتخاب کنید

مهم است که به منبع داده‌ای متصل شوید که ساختار داده منبع شما را به بهترین شکل مدیریت می‌کند:

  • اگر صفحه‌هایی که بار می‌کنید کلیدهای بعدی/قبلی را جاسازی می‌کنند، از PageKeyedDataSource استفاده کنید. برای مثال، اگر پست‌های رسانه‌های اجتماعی را از شبکه واکشی می‌کنید، ممکن است لازم باشد یک توکن nextPage را از یک بارگیری به بارگیری بعدی منتقل کنید.
  • اگر برای واکشی عنصر N+1 باید از داده‌های عنصر N استفاده کنید، از ItemKeyedDataSource استفاده کنید. برای مثال، اگر درحال واکشی نظرات رشته‌ای برای برنامه بحث هستید، ممکن است لازم باشد شناسه آخرین نظر را ارسال کنید تا محتوای نظر بعدی را دریافت کنید.
  • اگر نیاز دارید صفحه‌های داده را از هر مکانی که در فضای ذخیره‌سازی داده‌تان انتخاب می‌کنید واکشی کنید، از PositionalDataSource استفاده کنید. این کلاس از درخواست مجموعه‌ای از موارد داده که از هر مکانی که انتخاب می‌کنید شروع می‌شود پشتیبانی می‌کند. برای مثال، درخواست ممکن است ۵۰ مورد داده را که با مکان ۱۵۰۰ شروع می‌شود برگرداند.

اطلاع‌رسانی درصورت نامعتبر بودن داده‌ها

هنگام استفاده از «کتابخانه صفحه‌بندی»، لایه داده وظیفه دارد وقتی جدول یا ردیفی قدیمی می‌شود به لایه‌های دیگر برنامه شما اطلاع دهد. برای انجام این کار، invalidate() را از کلاس DataSource که برای برنامه‌تان انتخاب کرده‌اید فراخوانی کنید.

منابع داده خودتان را بسازید

اگر از راهکار داده‌های محلی سفارشی استفاده می‌کنید، یا اگر داده‌ها را مستقیماً از شبکه بار می‌کنید، می‌توانید یکی از زیرکلاس‌های DataSource را پیاده‌سازی کنید. تکه‌کد زیر منبع داده‌ای را نشان می‌دهد که براساس زمان شروع کنسرت مشخصی کلیدگذاری شده است:

کاتلین

class ConcertTimeDataSource() :
        ItemKeyedDataSource<Date, Concert>() {
    override fun getKey(item: Concert) = item.startTime

    override fun loadInitial(
            params: LoadInitialParams<Date>,
            callback: LoadInitialCallback<Concert>) {
        val items = fetchItems(params.requestedInitialKey,
                params.requestedLoadSize)
        callback.onResult(items)
    }

    override fun loadAfter(
            params: LoadParams<Date>,
            callback: LoadCallback<Concert>) {
        val items = fetchItemsAfter(
            date = params.key,
            limit = params.requestedLoadSize)
        callback.onResult(items)
    }
}

جاوا

public class ConcertTimeDataSource
        extends ItemKeyedDataSource<Date, Concert> {
    @NonNull
    @Override
    public Date getKey(@NonNull Concert item) {
        return item.getStartTime();
    }

    @Override
    public void loadInitial(@NonNull LoadInitialParams<Date> params,
            @NonNull LoadInitialCallback<Concert> callback) {
        List<Concert> items =
            fetchItems(params.key, params.requestedLoadSize);
        callback.onResult(items);
    }

    @Override
    public void loadAfter(@NonNull LoadParams<Date> params,
            @NonNull LoadCallback<Concert> callback) {
        List<Concert> items =
            fetchItemsAfter(params.key, params.requestedLoadSize);
        callback.onResult(items);
    }

سپس می‌توانید این داده‌های سفارشی‌شده را با ایجاد یک زیرکلاس واقعی از DataSource.Factory در اشیاء PagedList بار کنید. تکه‌کد زیر نحوه تولید نمونه‌های جدید از منبع داده سفارشی تعریف‌شده در تکه‌کد قبلی را نشان می‌دهد:

کاتلین

class ConcertTimeDataSourceFactory :
        DataSource.Factory<Date, Concert>() {
    val sourceLiveData = MutableLiveData<ConcertTimeDataSource>()
    var latestSource: ConcertDataSource?
    override fun create(): DataSource<Date, Concert> {
        latestSource = ConcertTimeDataSource()
        sourceLiveData.postValue(latestSource)
        return latestSource
    }
}

جاوا

public class ConcertTimeDataSourceFactory
        extends DataSource.Factory<Date, Concert> {
    private MutableLiveData<ConcertTimeDataSource> sourceLiveData =
            new MutableLiveData<>();

    private ConcertDataSource latestSource;

    @Override
    public DataSource<Date, Concert> create() {
        latestSource = new ConcertTimeDataSource();
        sourceLiveData.postValue(latestSource);
        return latestSource;
    }
}

نحوه عملکرد به‌روزرسانی‌های محتوا را درنظر بگیرید

هنگام ساختن اشیای PagedList قابل‌مشاهده، نحوه عملکرد به‌روزرسانی‌های محتوا را درنظر بگیرید. اگر داده‌ها را مستقیماً از پایگاه داده Room بار می‌کنید، به‌روزرسانی‌ها به‌طور خودکار به میانای کاربر برنامه‌تان ارسال می‌شود.

هنگام استفاده از میانای برنامه‌سازی کاربردی شبکه صفحه‌بندی‌شده، معمولاً تعامل کاربر، مانند «تند کشیدن برای بازآوری»، به‌عنوان سیگنالی برای باطل کردن DataSource که اخیراً استفاده کرده‌اید عمل می‌کند. سپس نمونه جدیدی از آن منبع داده را درخواست می‌کنید. تکه‌کد زیر این رفتار را نشان می‌دهد:

کاتلین

class ConcertActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        // ...
        concertTimeViewModel.refreshState.observe(this, Observer {
            // Shows one possible way of triggering a refresh operation.
            swipeRefreshLayout.isRefreshing =
                    it == MyNetworkState.LOADING
        })
        swipeRefreshLayout.setOnRefreshListener {
            concertTimeViewModel.invalidateDataSource()
        }
    }
}

class ConcertTimeViewModel(firstConcertStartTime: Date) : ViewModel() {
    val dataSourceFactory = ConcertTimeDataSourceFactory(firstConcertStartTime)
    val concertList: LiveData<PagedList<Concert>> =
            dataSourceFactory.toLiveData(
                pageSize = 50,
                fetchExecutor = myExecutor
            )

    fun invalidateDataSource() =
            dataSourceFactory.sourceLiveData.value?.invalidate()
}

جاوا

public class ConcertActivity extends AppCompatActivity {
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        // ...
        viewModel.getRefreshState()
                .observe(this, new Observer<NetworkState>() {
            // Shows one possible way of triggering a refresh operation.
            @Override
            public void onChanged(@Nullable MyNetworkState networkState) {
                swipeRefreshLayout.isRefreshing =
                        networkState == MyNetworkState.LOADING;
            }
        };

        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshListener() {
            @Override
            public void onRefresh() {
                viewModel.invalidateDataSource();
            }
        });
    }
}

public class ConcertTimeViewModel extends ViewModel {
    private LiveData<PagedList<Concert>> concertList;
    private DataSource<Date, Concert> mostRecentDataSource;

    public ConcertTimeViewModel(Date firstConcertStartTime) {
        ConcertTimeDataSourceFactory dataSourceFactory =
                new ConcertTimeDataSourceFactory(firstConcertStartTime);
        mostRecentDataSource = dataSourceFactory.create();
        concertList = new LivePagedListBuilder<>(dataSourceFactory, 50)
                .setFetchExecutor(myExecutor)
                .build();
    }

    public void invalidateDataSource() {
        mostRecentDataSource.invalidate();
    }
}

ارائه نگاشت داده‌ها

«کتابخانه صفحه‌بندی» از تبدیل‌های مبتنی بر مورد و مبتنی بر صفحه برای مواردی که توسط DataSource بار شده‌اند پشتیبانی می‌کند.

در تکه‌کد زیر، ترکیبی از نام کنسرت و تاریخ کنسرت به یک رشته واحد که حاوی نام و تاریخ است نگاشت می‌شود:

کاتلین

class ConcertViewModel : ViewModel() {
    val concertDescriptions : LiveData<PagedList<String>>
        init {
            val concerts = database.allConcertsFactory()
                    .map { "${it.name} - ${it.date}" }
                    .toLiveData(pageSize = 50)
        }
}

جاوا

public class ConcertViewModel extends ViewModel {
    private LiveData<PagedList<String>> concertDescriptions;

    public ConcertViewModel(MyDatabase database) {
        DataSource.Factory<Integer, Concert> factory =
                database.allConcertsFactory().map(concert ->
                    concert.getName() + "-" + concert.getDate());
        concertDescriptions = new LivePagedListBuilder<>(
            factory, /* page size */ 50).build();
    }
}

این کار زمانی مفید است که بخواهید موارد را پس‌از بار شدن، بسته‌بندی، تبدیل، یا آماده کنید. چون این کار در مجری واکشی انجام می‌شود، می‌توانید کارهای بالقوه پرهزینه، مثل خواندن از دیسک یا پرسمان از پایگاه داده جداگانه، انجام دهید.

ارائه کردن بازخورد

بازخورد و ایده‌هایتان را ازطریق این منابع با ما هم‌رسانی کنید:

ردیاب مشکل
مشکلات را گزارش کنید تا بتوانیم اشکالات را برطرف کنیم.

منابع بیشتر

برای کسب اطلاعات بیشتر درباره «کتابخانه صفحه‌بندی»، به منابع زیر مراجعه کنید.

نمونه‌ها

Codelabs

ویدیوها