Paging 2 kitaplığına genel bakış   Android Jetpack'in bir parçasıdır.

Paging Library, verileri küçük parçalar halinde yükleyip görüntülemenize yardımcı olur. Kısmi verilerin isteğe bağlı olarak yüklenmesi, ağ bant genişliği ve sistem kaynaklarının kullanımını azaltır.

Bu kılavuzda, kitaplığın işleyiş şekline genel bir bakışın yanı sıra kitaplıkla ilgili birkaç kavramsal örnek verilmiştir. Bu kitaplığın işleyiş şekliyle ilgili eksiksiz örnekleri görüntülemek için ek kaynaklar bölümündeki codelab'i ve örnekleri deneyin.

Kurulum

Sayfalama bileşenlerini Android uygulamanıza aktarmak için uygulamanızın build.gradle dosyasına aşağıdaki bağımlılıkları ekleyin:

Groovy

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
}

Kotlin

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
}

Kitaplık mimarisi

Bu bölümde, sayfalama kitaplığının ana bileşenleri açıklanmakta ve gösterilmektedir.

PagedList

Paging Library'nin temel bileşeni, uygulamanızın veri parçalarını veya sayfalarını yükleyen PagedList sınıfıdır. Daha fazla veri gerektiğinde mevcut PagedList nesnesine sayfa olarak eklenir. Yüklenen veriler değişirse LiveData veya RxJava2 tabanlı bir nesneden gözlemlenebilir veri tutucuya yeni bir PagedList örneği gönderilir. PagedList nesneleri oluşturulurken uygulamanızın kullanıcı arayüzü, kullanıcı arayüzü denetleyicilerinizin yaşam döngülerine uygun şekilde içeriklerini sunar.

Aşağıdaki kod snippet'i, uygulamanızın görünüm modelini, PagedList nesnelerinin LiveData tutucusunu kullanarak verileri yükleyip sunacak şekilde nasıl yapılandırabileceğinizi gösterir:

Kotlin

class ConcertViewModel(concertDao: ConcertDao) : ViewModel() {
    val concertList: LiveData<PagedList<Concert>> =
            concertDao.concertsByDate().toLiveData(pageSize = 50)
}

Java

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();
    }
}

Veriler

Her PagedList örneği, uygulamanızın verilerinin güncel bir anlık görüntüsünü ilgili DataSource nesnesinden yükler. Veriler, uygulamanızın arka ucundan veya veritabanından PagedList nesnesine aktarılır.

Aşağıdaki örnekte, uygulamanızın verilerini düzenlemek için Room kalıcılık kitaplığı kullanılmaktadır. Ancak verilerinizi başka bir yöntemle depolamak isterseniz kendi veri kaynağı fabrikanızı da sağlayabilirsiniz.

Kotlin

@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>
}

Java

@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 nesnelerine nasıl veri yükleyebileceğiniz hakkında daha fazla bilgi edinmek için Sayfalı verileri yükleme ile ilgili kılavuzu inceleyin.

UI

PagedList sınıfı, öğeleri RecyclerView içine yüklemek için PagedListAdapter ile birlikte çalışır. Bu sınıflar, içerikleri yüklendikçe getirip görüntülemek, görünüm dışı içerikleri önceden getirmek ve içerik değişikliklerini canlandırmak için birlikte çalışır.

Daha fazla bilgi edinmek için Sayfalı listeleri görüntüleme ile ilgili kılavuzu inceleyin.

Farklı veri mimarilerini destekleme

Paging kitaplığı aşağıdaki veri mimarilerini destekler:

  • Yalnızca bir arka uç sunucusundan sunulur.
  • Yalnızca cihaz üzerindeki bir veritabanında depolanır.
  • Cihaz üzerindeki veritabanını önbellek olarak kullanan diğer kaynakların bir kombinasyonu.

Şekil 1'de, bu mimari senaryoların her birinde verilerin nasıl aktığı gösterilmektedir. Yalnızca ağ veya yalnızca veritabanı çözümü kullanıldığında veriler doğrudan uygulamanızın kullanıcı arayüzü modeline akar. Kombine bir yaklaşım kullanıyorsanız veriler arka uç sunucunuzdan cihaz üzerindeki bir veritabanına, ardından uygulamanızın kullanıcı arayüzü modeline akar. Her veri akışının uç noktasında yüklenecek veri kalmadığında, veriyi sağlayan bileşenden daha fazla veri istenir. Örneğin, cihaz üzerindeki bir veritabanında veri kalmadığında sunucudan daha fazla veri ister.

Desteklenen Paging Library mimarilerinde veri akışı
Şekil 1. Paging kitaplığının desteklediği mimarilerin her birinde verilerin nasıl aktığı.

Bu bölümün geri kalanında, her bir veri akışı kullanım alanını yapılandırmaya yönelik öneriler sunulmaktadır.

Yalnızca ağ

Arka uç sunucusundaki verileri görüntülemek için Retrofit API'nin eşzamanlı sürümünü kullanarak bilgileri kendi özel DataSource nesnenize yükleyin.

Yalnızca veritabanı

RecyclerView'inizi, tercihen Room kalıcılık kitaplığını kullanarak yerel depolama alanını gözlemleyecek şekilde ayarlayın. Böylece, uygulamanızın veritabanına veri eklendiğinde veya veritabanındaki veriler değiştirildiğinde bu değişiklikler, verileri gösteren RecyclerView öğesine otomatik olarak yansıtılır.

Ağ ve veritabanı

Veritabanını gözlemlemeye başladıktan sonra PagedList.BoundaryCallback kullanarak veritabanında veri kalmadığında bildirim alabilirsiniz. Ardından, ağınızdan daha fazla öğe getirip bunları veritabanına ekleyebilirsiniz. Kullanıcı arayüzünüz veritabanını gözlemliyorsa yapmanız gereken tek şey budur.

Ağ hatalarını giderme

Paging kitaplığını kullanarak görüntülediğiniz verileri getirmek veya sayfalamak için bir ağ kullanırken ağı her zaman "kullanılabilir" veya "kullanılamaz" olarak değerlendirmemek önemlidir. Birçok bağlantı kesintili veya kararsızdır:

  • Belirli bir sunucu, ağ isteğine yanıt vermeyebilir.
  • Cihaz, yavaş veya zayıf bir ağa bağlı olabilir.

Bunun yerine, uygulamanız her isteği hatalara karşı kontrol etmeli ve ağın kullanılamadığı durumlarda mümkün olduğunca sorunsuz bir şekilde kurtarılmalıdır. Örneğin, veri yenileme adımı çalışmazsa kullanıcıların seçebileceği bir "yeniden dene" düğmesi sağlayabilirsiniz. Veri sayfalama adımı sırasında bir hata oluşursa sayfalama isteklerini otomatik olarak yeniden denemek en iyisidir.

Mevcut uygulamanızı güncelleme

Uygulamanız zaten bir veritabanından veya arka uç kaynağından veri kullanıyorsa doğrudan Paging Library'nin sağladığı işlevlere yükseltme yapabilirsiniz. Bu bölümde, yaygın olarak kullanılan mevcut bir tasarıma sahip bir uygulamanın nasıl yükseltileceği gösterilmektedir.

Özel sayfalama çözümleri

Uygulamanızın veri kaynağından küçük veri alt kümeleri yüklemek için özel işlevler kullanıyorsanız bu mantığı PagedList sınıfındaki mantıkla değiştirebilirsiniz. PagedList örnekleri, yaygın veri kaynaklarına yerleşik bağlantılar sunar. Bu örnekler, uygulamanızın kullanıcı arayüzüne ekleyebileceğiniz RecyclerView nesneleri için de bağdaştırıcılar sağlar.

Veriler sayfalar yerine listeler kullanılarak yüklenir.

Kullanıcı arayüzünüzün bağdaştırıcısı için destekleyici veri yapısı olarak bellek içi liste kullanıyorsanız listedeki öğe sayısı büyük olabilirse PagedList sınıfını kullanarak veri güncellemelerini gözlemleyebilirsiniz. PagedList örnekleri, veri güncellemelerini uygulamanızın kullanıcı arayüzüne aktarmak için LiveData<PagedList> veya Observable<List> kullanarak yükleme sürelerini ve bellek kullanımını en aza indirebilir. Daha da iyisi, uygulamanızdaki bir List nesnesini PagedList nesnesiyle değiştirmek, uygulamanızın kullanıcı arayüzü yapısında veya veri güncelleme mantığında herhangi bir değişiklik yapılmasını gerektirmez.

CursorAdapter kullanarak bir veri imlecini liste görünümüyle ilişkilendirme

Uygulamanız, Cursor'dan gelen verileri ListView ile ilişkilendirmek için CursorAdapter kullanabilir. Bu durumda, genellikle uygulamanızın liste kullanıcı arayüzü kapsayıcısı olarak ListView yerine RecyclerView kullanmanız ve ardından Cursor bileşenini, Cursor örneklerinin bir SQLite veritabanına erişip erişmediğine bağlı olarak Room veya PositionalDataSource ile değiştirmeniz gerekir.

Spinner örnekleriyle çalışırken olduğu gibi bazı durumlarda yalnızca adaptörün kendisini sağlarsınız. Ardından bir kitaplık, bu adaptöre yüklenen verileri alır ve verileri size gösterir. Bu gibi durumlarda, bağdaştırıcınızın veri türünü LiveData<PagedList> olarak değiştirin ve ardından bir kitaplık sınıfının bu öğeleri kullanıcı arayüzünde şişirmesini denemeden önce bu listeyi bir ArrayAdapter nesnesine sarmalayın.

AsyncListUtil kullanarak içeriği eşzamansız olarak yükleme

Bilgi gruplarını eşzamansız olarak yüklemek ve görüntülemek için AsyncListUtil nesneleri kullanıyorsanız Paging Library, veri yüklemeyi basitleştirir:

  • Verilerinizin konumsal olması gerekmez. Paging kitaplığı, ağın sağladığı anahtarları kullanarak verileri doğrudan arka ucunuzdan yüklemenize olanak tanır.
  • Verileriniz sayılamayacak kadar büyük olabilir. Paging Library'yi kullanarak verileri, hiç veri kalmayana kadar sayfalara yükleyebilirsiniz.
  • Verilerinizi daha kolay inceleyebilirsiniz. Paging kitaplığı, uygulamanızın ViewModel'inin tuttuğu verileri gözlemlenebilir bir veri yapısında sunabilir.

Veritabanı örnekleri

Aşağıdaki kod snippet'leri, tüm parçaların birlikte çalışmasını sağlamanın birkaç olası yolunu göstermektedir.

LiveData kullanarak sayfalara ayrılmış verileri gözlemleme

Aşağıdaki kod snippet'inde tüm parçaların birlikte nasıl çalıştığı gösterilmektedir. Veritabanına konser etkinlikleri eklendikçe, kaldırıldıkça veya değiştirildikçe RecyclerView içeriği otomatik ve verimli bir şekilde güncellenir:

Kotlin

@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
        }
    }
}

Java

@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 kullanarak sayfalara ayrılmış verileri gözlemleme

LiveData yerine RxJava2 kullanmayı tercih ederseniz bunun yerine bir Observable veya Flowable nesnesi oluşturabilirsiniz:

Kotlin

class ConcertViewModel(concertDao: ConcertDao) : ViewModel() {
    val concertList: Observable<PagedList<Concert>> =
            concertDao.concertsByDate().toObservable(pageSize = 50)
}

Java

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();
    }
}

Ardından, aşağıdaki snippet'te yer alan kodu kullanarak verileri gözlemlemeye başlayabilir ve durdurabilirsiniz:

Kotlin

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()
    }
}

Java

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 ve ConcertAdapter kodu, RxJava2 tabanlı bir çözüm için LiveData tabanlı bir çözümle aynıdır.

Geri bildirim gönderin

Geri bildirimlerinizi ve fikirlerinizi aşağıdaki kaynaklar aracılığıyla bizimle paylaşabilirsiniz:

Issue Tracker
Hataları düzeltebilmemiz için sorunları bildirin.

Ek kaynaklar

Paging Library hakkında daha fazla bilgi edinmek için aşağıdaki kaynaklara göz atın.

Örnekler

Codelab uygulamaları

Videolar