نمای کلی کتابخانه 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 برنامه شما جریان می یابد. هر چند وقت یک‌بار، نقطه پایانی هر جریان داده برای بارگذاری تمام می‌شود، در این مرحله داده‌های بیشتری را از مؤلفه‌ای که داده‌ها را ارائه می‌کند درخواست می‌کند. به عنوان مثال، زمانی که یک پایگاه داده روی دستگاه با کمبود داده مواجه می شود، داده های بیشتری را از سرور درخواست می کند.

نمودارهای جریان داده
شکل 1. نحوه جریان داده ها از طریق هر یک از معماری هایی که کتابخانه صفحه بندی پشتیبانی می کند

بقیه این بخش توصیه هایی را برای پیکربندی هر مورد استفاده از جریان داده ارائه می دهد.

فقط شبکه

برای نمایش داده‌ها از یک سرور پشتیبان، از نسخه همگام 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

ویدیوها

{% کلمه به کلمه %} {% آخر کلمه %} {% کلمه به کلمه %} {% آخر کلمه %}