نمای کلی کتابخانه Paging 2 بخشی از Android Jetpack .
کتابخانه صفحهبندی به شما کمک میکند تا تکههای کوچکی از دادهها را در یک زمان بارگیری و نمایش دهید. بارگذاری جزئی داده بر اساس تقاضا، استفاده از پهنای باند شبکه و منابع سیستم را کاهش می دهد.
این راهنما چندین مثال مفهومی از کتابخانه را به همراه یک نمای کلی از نحوه عملکرد آن ارائه می دهد. برای مشاهده نمونههای کامل از نحوه عملکرد این کتابخانه، آزمایشگاه کد و نمونههایی را از بخش منابع اضافی امتحان کنید.
راه اندازی
برای وارد کردن اجزای Paging به برنامه Android خود، وابستگی های زیر را به فایل build.gradle
برنامه خود اضافه کنید:
شیار
dependencies { def paging_version = "2.1.2" implementation "androidx.paging:paging-runtime:$paging_version" // For Kotlin use paging-runtime-ktx // alternatively - without Android dependencies for testing testImplementation "androidx.paging:paging-common:$paging_version" // For Kotlin use paging-common-ktx // optional - RxJava support implementation "androidx.paging:paging-rxjava2:$paging_version" // For Kotlin use paging-rxjava2-ktx }
کاتلین
dependencies { val paging_version = "2.1.2" implementation("androidx.paging:paging-runtime:$paging_version") // For Kotlin use paging-runtime-ktx // alternatively - without Android dependencies for testing testImplementation("androidx.paging:paging-common:$paging_version") // For Kotlin use paging-common-ktx // optional - RxJava support implementation("androidx.paging:paging-rxjava2:$paging_version") // For Kotlin use paging-rxjava2-ktx }
معماری کتابخانه
این بخش اجزای اصلی کتابخانه صفحهبندی را توصیف و نشان میدهد.
PagedList
جزء کلیدی کتابخانه صفحهبندی کلاس PagedList
است که تکههایی از دادهها یا صفحات برنامه شما را بارگیری میکند. همانطور که داده های بیشتری مورد نیاز است، در شیء موجود PagedList
صفحه می شود. اگر داده های بارگذاری شده تغییر کند، نمونه جدیدی از PagedList
از یک شی مبتنی بر LiveData
یا RxJava2 به دارنده داده قابل مشاهده ارسال می شود. همانطور که اشیاء PagedList
تولید می شوند، UI برنامه شما محتویات آنها را ارائه می دهد، همه اینها در عین احترام به چرخه عمر کنترلرهای UI شما.
قطعه کد زیر نشان می دهد که چگونه می توانید مدل view برنامه خود را برای بارگیری و ارائه داده ها با استفاده از دارنده LiveData
اشیاء PagedList
پیکربندی کنید:
کاتلین
class ConcertViewModel(concertDao: ConcertDao) : ViewModel() { val concertList: LiveData<PagedList<Concert>> = concertDao.concertsByDate().toLiveData(pageSize = 50) }
جاوا
public class ConcertViewModel extends ViewModel { private ConcertDao concertDao; public final LiveData<PagedList<Concert>> concertList; // Creates a PagedList object with 50 items per page. public ConcertViewModel(ConcertDao concertDao) { this.concertDao = concertDao; concertList = new LivePagedListBuilder<>( concertDao.concertsByDate(), 50).build(); } }
داده ها
هر نمونه از PagedList
یک عکس فوری بهروز از دادههای برنامه شما را از شی DataSource
مربوطه بارگیری میکند. داده ها از باطن یا پایگاه داده برنامه شما به شی PagedList
جریان می یابد.
مثال زیر از کتابخانه ماندگاری اتاق برای سازماندهی داده های برنامه شما استفاده می کند، اما اگر می خواهید داده های خود را با استفاده از ابزار دیگری ذخیره کنید، می توانید کارخانه منبع داده خود را نیز ارائه دهید .
کاتلین
@Dao interface ConcertDao { // The Int type parameter tells Room to use a PositionalDataSource object. @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. @Query("SELECT * FROM concerts ORDER BY date DESC") DataSource.Factory<Integer, Concert> concertsByDate(); }
برای اطلاعات بیشتر در مورد نحوه بارگیری داده ها در اشیاء PagedList
، به راهنمای نحوه بارگیری داده های صفحه شده مراجعه کنید.
UI
کلاس PagedList
با یک PagedListAdapter
کار می کند تا آیتم ها را در RecyclerView
بارگذاری کند. این کلاسها با هم کار میکنند تا محتوا را هنگام بارگیری واکشی و نمایش دهند، محتوای خارج از دید را از قبل واکشی میکنند و تغییرات محتوا را متحرک میکنند.
برای کسب اطلاعات بیشتر، راهنمای نحوه نمایش لیست های صفحه شده را ببینید.
پشتیبانی از معماری داده های مختلف
کتابخانه صفحهبندی از معماریهای داده زیر پشتیبانی میکند:
- فقط از یک سرور باطن ارائه می شود.
- فقط در پایگاه داده روی دستگاه ذخیره می شود.
- ترکیبی از منابع دیگر، با استفاده از پایگاه داده روی دستگاه به عنوان کش.
شکل 1 نحوه جریان داده ها را در هر یک از این سناریوهای معماری نشان می دهد. در مورد راه حل فقط شبکه یا فقط پایگاه داده، داده ها مستقیماً به مدل UI برنامه شما جریان می یابد. اگر از یک رویکرد ترکیبی استفاده می کنید، داده ها از سرور باطن شما به یک پایگاه داده روی دستگاه و سپس به مدل UI برنامه شما جریان می یابد. هر چند وقت یکبار، نقطه پایانی هر جریان داده برای بارگذاری تمام میشود، در این مرحله دادههای بیشتری را از مؤلفهای که دادهها را ارائه میکند درخواست میکند. به عنوان مثال، زمانی که یک پایگاه داده روی دستگاه با کمبود داده مواجه می شود، داده های بیشتری را از سرور درخواست می کند.
بقیه این بخش توصیه هایی را برای پیکربندی هر مورد استفاده از جریان داده ارائه می دهد.
فقط شبکه
برای نمایش دادهها از یک سرور پشتیبان، از نسخه همگام Retrofit API برای بارگیری اطلاعات در شیء DataSource
سفارشی خود استفاده کنید.
فقط پایگاه داده
RecyclerView
خود را برای مشاهده فضای ذخیرهسازی محلی، ترجیحاً با استفاده از کتابخانه ماندگاری اتاق تنظیم کنید. به این ترتیب، هر زمان که دادهها در پایگاه داده برنامه شما درج یا اصلاح میشوند، این تغییرات بهطور خودکار در RecyclerView
که این دادهها را نمایش میدهد منعکس میشود.
شبکه و پایگاه داده
پس از شروع مشاهده پایگاه داده، می توانید با استفاده از PagedList.BoundaryCallback
به زمانی که پایگاه داده فاقد داده است گوش دهید. سپس می توانید موارد بیشتری را از شبکه خود دریافت کرده و آنها را در پایگاه داده وارد کنید. اگر UI شما پایگاه داده را مشاهده می کند، این تنها کاری است که باید انجام دهید.
رسیدگی به خطاهای شبکه
هنگام استفاده از شبکه برای واکشی یا صفحهبندی دادههایی که با استفاده از کتابخانه صفحهبندی نمایش میدهید، مهم است که شبکه را همیشه «در دسترس» یا «غیرقابل دسترس» تلقی نکنید، زیرا بسیاری از اتصالات متناوب یا پوستهپوسته هستند:
- ممکن است یک سرور خاص به درخواست شبکه پاسخ ندهد.
- ممکن است دستگاه به شبکه ای که کند یا ضعیف است متصل باشد.
درعوض، برنامه شما باید هر درخواستی را برای خرابی بررسی کند و در مواردی که شبکه در دسترس نیست، تا حد امکان با ظرافت بازیابی کند. به عنوان مثال، میتوانید یک دکمه «تلاش مجدد» را در اختیار کاربران قرار دهید تا در صورت کار نکردن مرحله بهروزرسانی داده، آن را انتخاب کنند. اگر در مرحله صفحهبندی دادهها خطایی رخ داد، بهتر است درخواستهای صفحهبندی را دوباره بهطور خودکار امتحان کنید.
برنامه موجود خود را به روز کنید
اگر برنامه شما قبلاً دادههای یک پایگاه داده یا منبع پشتیبان را مصرف میکند، میتوانید مستقیماً به عملکردی که کتابخانه صفحهبندی ارائه میکند ارتقا دهید. این بخش نشان می دهد که چگونه می توان برنامه ای را که دارای طراحی مشترک موجود است ارتقا داد.
راه حل های صفحه بندی سفارشی
اگر از عملکرد سفارشی برای بارگیری زیرمجموعه های کوچک داده از منبع داده برنامه خود استفاده می کنید، می توانید این منطق را با منطق کلاس PagedList
جایگزین کنید. نمونههای PagedList
اتصالات داخلی را به منابع داده رایج ارائه میدهند. این نمونهها همچنین آداپتورهایی را برای اشیاء RecyclerView
ارائه میکنند که ممکن است در UI برنامه خود قرار دهید.
داده ها با استفاده از لیست ها به جای صفحات بارگیری می شوند
اگر از یک لیست در حافظه به عنوان ساختار داده پشتیبان برای آداپتور UI خود استفاده می کنید، اگر تعداد موارد موجود در لیست می تواند زیاد شود، به روز رسانی داده ها را با استفاده از کلاس PagedList
مشاهده کنید. نمونههایی از PagedList
میتوانند از LiveData<PagedList>
یا Observable<List>
برای ارسال بهروزرسانیهای داده به رابط کاربری برنامه شما استفاده کنند و زمان بارگذاری و مصرف حافظه را به حداقل برسانند. با این حال بهتر است، جایگزین کردن یک شی List
با یک شی PagedList
در برنامه شما نیازی به تغییر در ساختار رابط کاربری برنامه شما یا منطق به روز رسانی داده ها ندارد.
با استفاده از CursorAdapter، مکاننمای داده را با نمای فهرست مرتبط کنید
ممکن است برنامه شما از CursorAdapter
برای مرتبط کردن داده های Cursor
با ListView
استفاده کند. در آن صورت، معمولاً باید از ListView
به RecyclerView
به عنوان محفظه UI لیست برنامه خود مهاجرت کنید، سپس مولفه Cursor
را با Room یا PositionalDataSource
جایگزین کنید، بسته به اینکه آیا نمونه هایی از Cursor
به پایگاه داده SQLite دسترسی دارند یا خیر.
در برخی شرایط، مانند هنگام کار با نمونههای Spinner
، فقط خود آداپتور را ارائه میکنید. سپس یک کتابخانه داده هایی را که در آن آداپتور بارگذاری شده است می گیرد و داده ها را برای شما نمایش می دهد. در این شرایط، نوع دادههای آداپتور خود را به LiveData<PagedList>
تغییر دهید، سپس این لیست را در یک شیء ArrayAdapter
بپیچید، قبل از اینکه یک کلاس کتابخانهای این موارد را در یک رابط کاربری ایجاد کنید.
بارگیری محتوا به صورت ناهمزمان با استفاده از AsyncListUtil
اگر از اشیاء AsyncListUtil
برای بارگیری و نمایش گروههای اطلاعات به صورت ناهمزمان استفاده میکنید، کتابخانه Paging به شما امکان میدهد دادهها را راحتتر بارگیری کنید:
- داده های شما نیازی به موقعیتی ندارند. کتابخانه صفحهبندی به شما امکان میدهد با استفاده از کلیدهایی که شبکه فراهم میکند، دادهها را مستقیماً از باطن خود بارگیری کنید.
- داده های شما می تواند به طور غیرقابل شمارشی بزرگ باشد. با استفاده از کتابخانه صفحهبندی، میتوانید دادهها را در صفحات بارگذاری کنید تا زمانی که هیچ دادهای باقی نماند.
- شما می توانید داده های خود را راحت تر مشاهده کنید. کتابخانه Paging میتواند دادههای شما را که ViewModel برنامه شما در یک ساختار داده قابل مشاهده نگه میدارد، ارائه دهد.
نمونه های پایگاه داده
تکههای کد زیر چندین روش ممکن برای کار کردن همه قطعات با هم را نشان میدهند.
مشاهده داده های صفحه بندی شده با استفاده از LiveData
قطعه کد زیر تمام قطعات را که با هم کار می کنند نشان می دهد. با اضافه شدن، حذف یا تغییر رویدادهای کنسرت در پایگاه داده، محتوای موجود در RecyclerView
به طور خودکار و کارآمد به روز می شود:
کاتلین
@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> } class ConcertViewModel(concertDao: ConcertDao) : ViewModel() { val concertList: LiveData<PagedList<Concert>> = concertDao.concertsByDate().toLiveData(pageSize = 50) } class ConcertActivity : AppCompatActivity() { public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Use the 'by viewModels()' Kotlin property delegate // from the activity-ktx artifact val viewModel: ConcertViewModel by viewModels() val recyclerView = findViewById(R.id.concert_list) val adapter = ConcertAdapter() viewModel.concertList.observe(this, PagedList(adapter::submitList)) recyclerView.setAdapter(adapter) } } class ConcertAdapter() : PagedListAdapter<Concert, ConcertViewHolder>(DIFF_CALLBACK) { fun onBindViewHolder(holder: ConcertViewHolder, position: Int) { val concert: Concert? = getItem(position) // Note that "concert" is a placeholder if it's null. holder.bindTo(concert) } companion object { private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Concert>() { // Concert details may have changed if reloaded from the database, // but ID is fixed. override fun areItemsTheSame(oldConcert: Concert, newConcert: Concert) = oldConcert.id == newConcert.id override fun areContentsTheSame(oldConcert: Concert, newConcert: Concert) = oldConcert == newConcert } } }
جاوا
@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(); } public class ConcertViewModel extends ViewModel { private ConcertDao concertDao; public final LiveData<PagedList<Concert>> concertList; public ConcertViewModel(ConcertDao concertDao) { this.concertDao = concertDao; concertList = new LivePagedListBuilder<>( concertDao.concertsByDate(), /* page size */ 50).build(); } } public class ConcertActivity extends AppCompatActivity { @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); ConcertViewModel viewModel = new ViewModelProvider(this).get(ConcertViewModel.class); RecyclerView recyclerView = findViewById(R.id.concert_list); ConcertAdapter adapter = new ConcertAdapter(); viewModel.concertList.observe(this, adapter::submitList); recyclerView.setAdapter(adapter); } } public class ConcertAdapter extends PagedListAdapter<Concert, ConcertViewHolder> { protected ConcertAdapter() { super(DIFF_CALLBACK); } @Override public void onBindViewHolder(@NonNull ConcertViewHolder holder, int position) { Concert concert = getItem(position); if (concert != null) { holder.bindTo(concert); } else { // Null defines a placeholder item - PagedListAdapter automatically // invalidates this row when the actual object is loaded from the // database. holder.clear(); } } private static DiffUtil.ItemCallback<Concert> DIFF_CALLBACK = new DiffUtil.ItemCallback<Concert>() { // Concert details may have changed if reloaded from the database, // but ID is fixed. @Override public boolean areItemsTheSame(Concert oldConcert, Concert newConcert) { return oldConcert.getId() == newConcert.getId(); } @Override public boolean areContentsTheSame(Concert oldConcert, Concert newConcert) { return oldConcert.equals(newConcert); } }; }
مشاهده داده های صفحه بندی شده با استفاده از RxJava2
اگر ترجیح می دهید از RxJava2 به جای LiveData
استفاده کنید، در عوض می توانید یک شی Observable
یا Flowable
ایجاد کنید:
کاتلین
class ConcertViewModel(concertDao: ConcertDao) : ViewModel() { val concertList: Observable<PagedList<Concert>> = concertDao.concertsByDate().toObservable(pageSize = 50) }
جاوا
public class ConcertViewModel extends ViewModel { private ConcertDao concertDao; public final Observable<PagedList<Concert>> concertList; public ConcertViewModel(ConcertDao concertDao) { this.concertDao = concertDao; concertList = new RxPagedListBuilder<>( concertDao.concertsByDate(), /* page size */ 50) .buildObservable(); } }
سپس می توانید مشاهده داده ها را با استفاده از کد موجود در قطعه زیر شروع و متوقف کنید:
کاتلین
class ConcertActivity : AppCompatActivity() { private val adapter = ConcertAdapter() // Use the 'by viewModels()' Kotlin property delegate // from the activity-ktx artifact private val viewModel: ConcertViewModel by viewModels() private val disposable = CompositeDisposable() public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val recyclerView = findViewById(R.id.concert_list) recyclerView.setAdapter(adapter) } override fun onStart() { super.onStart() disposable.add(viewModel.concertList .subscribe(adapter::submitList))) } override fun onStop() { super.onStop() disposable.clear() } }
جاوا
public class ConcertActivity extends AppCompatActivity { private ConcertAdapter adapter = new ConcertAdapter(); private ConcertViewModel viewModel; private CompositeDisposable disposable = new CompositeDisposable(); @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); RecyclerView recyclerView = findViewById(R.id.concert_list); viewModel = new ViewModelProvider(this).get(ConcertViewModel.class); recyclerView.setAdapter(adapter); } @Override protected void onStart() { super.onStart(); disposable.add(viewModel.concertList .subscribe(adapter.submitList(flowableList) )); } @Override protected void onStop() { super.onStop(); disposable.clear(); } }
کد ConcertDao
و ConcertAdapter
برای راه حل های مبتنی بر RxJava2 مانند راه حل های مبتنی بر LiveData
یکسان است.
بازخورد ارائه دهید
نظرات و ایده های خود را از طریق این منابع با ما در میان بگذارید:
- ردیاب مشکل
- مشکلات را گزارش کنید تا بتوانیم اشکالات را برطرف کنیم.
منابع اضافی
برای کسب اطلاعات بیشتر در مورد کتابخانه صفحه بندی، به منابع زیر مراجعه کنید.
نمونه ها
Codelabs
ویدیوها
- Android Jetpack: لیست های بی نهایت را با RecyclerView و Paging مدیریت کنید (Google I/O '18)
- Android Jetpack: پیجینگ
برای شما توصیه می شود
- توجه: وقتی جاوا اسکریپت خاموش است، متن پیوند نمایش داده می شود
- مهاجرت به صفحه 3
- نمایش لیست های صفحه بندی شده
- داده های صفحه بندی شده را جمع آوری کنید