Ağ ve veritabanı sayfası

Ağ bağlantıları güvenilir olmadığında veya kullanıcı çevrimdışıyken uygulamanızın kullanılabildiğinden emin olarak daha iyi bir kullanıcı deneyimi sunun. Bunu yapmanın bir yolu, aynı anda hem ağdan hem de yerel bir veritabanından sayfa oluşturmaktır. Bu şekilde, uygulamanız kullanıcı arayüzünü yerel bir veritabanı önbelleğinden yönlendirir ve yalnızca veritabanında başka veri kalmadığında ağa istekte bulunur.

Bu kılavuzda, Room kalıcılık kitaplığı ve Paging kitaplığının temel kullanımı hakkında bilgi sahibi olduğunuz varsayılmaktadır.

Veri yüklemelerini koordine etme

Paging kitaplığı, bu kullanım alanı için RemoteMediator bileşenini sağlar. RemoteMediator, uygulama önbelleğe alınmış verileri tükendiğinde Paging kitaplığından gelen bir sinyal görevi görür. Bu sinyali kullanarak ağdan ek veriler yükleyebilir ve bunları yerel veritabanında saklayabilirsiniz. PagingSource, bu verileri yükleyip kullanıcı arayüzünde görüntülenmek üzere sağlayabilir.

Ek veriye ihtiyaç duyulduğunda Paging kitaplığı, RemoteMediator uygulamasından load() yöntemini çağırır. Bu, askıya alma işlevi olduğundan uzun süren işlemleri güvenle gerçekleştirebilirsiniz. Bu işlev genellikle yeni verileri bir ağ kaynağından getirir ve yerel depolama alanına kaydeder.

Bu işlem yeni verilerle çalışır ancak zamanla veritabanında depolanan verilerin geçersiz kılınması gerekir. Örneğin, kullanıcı manuel olarak yenileme işlemi başlattığında bu durum ortaya çıkar. Bu, load() yöntemine iletilen LoadType özelliğiyle gösterilir. LoadType, RemoteMediator'e mevcut verileri yenilemesi mi yoksa mevcut listeye eklenmesi veya listenin başına eklenmesi gereken ek verileri mi getirmesi gerektiğini bildirir.

Bu şekilde RemoteMediator, uygulamanızın kullanıcıların görmek istediği verileri uygun sırada yüklemesini sağlar.

Sayfalama yaşam döngüsü

Doğrudan ağdan sayfalama yapıldığında PagingSource verileri yükler ve LoadResult nesnesini döndürür. PagingSource uygulaması, pagingSourceFactory parametresi aracılığıyla Pager işlevine iletilir.

Kullanıcı arayüzü yeni veriler gerektirdiğinden Pager, PagingSource'den load() yöntemini çağırır ve yeni verileri kapsayan bir PagingData nesne akışı döndürür. Her PagingData nesnesi, görüntülenmek üzere kullanıcı arayüzüne gönderilmeden önce genellikle ViewModel içinde önbelleğe alınır.

Şekil 1. PagingSource ve RemoteMediator ile sayfalama yaşam döngüsünün şeması.

RemoteMediator bu veri akışını değiştirir. PagingSource, verileri yüklemeye devam eder ancak sayfalandırılmış veriler tükendiğinde Paging kitaplığı, ağ kaynağındaki yeni verileri yüklemek için RemoteMediator'yi tetikler. RemoteMediator, yeni verileri yerel veritabanında depolar. Bu nedenle, ViewModel içinde bellek içi önbellek kullanılması gereksizdir. Son olarak, PagingSource kendisini geçersiz kılar ve Pager, veritabanından yeni verileri yüklemek için yeni bir örnek oluşturur.

Temel kullanım

Uygulamanızın, öğe anahtarlı bir ağ veri kaynağından alınan User öğe sayfalarını Room veritabanında depolanan yerel bir önbelleğe yüklemesini istediğinizi varsayalım.

RemoteMediator uygulaması, ağdan alınan sayfalandırılmış verilerin veritabanına yüklenmesine yardımcı olur ancak verileri doğrudan kullanıcı arayüzüne yüklemez. Bunun yerine uygulama, veritabanını doğruluk kaynağı olarak kullanır. Diğer bir deyişle, uygulama yalnızca veri tabanında önbelleğe alınmış verileri gösterir. PagingSource uygulaması (örneğin, Room tarafından oluşturulan bir uygulama), önbelleğe alınmış verilerin veritabanından kullanıcı arayüzüne yüklenmesini sağlar.

Oda varlıkları oluşturma

İlk adım, ağ veri kaynağındaki sayfalandırılmış verilerin yerel önbelleğini tutan bir veritabanı tanımlamak için Room persistence kitaplığını kullanmaktır. RoomDatabaseRoom kullanarak verileri yerel veritabanına kaydetme bölümünde açıklandığı şekilde uygulayarak başlayın.

Ardından, Oda varlıklarını kullanarak verileri tanımlama bölümünde açıklandığı gibi, liste öğelerinin tablosunu temsil edecek bir Oda varlığı tanımlayın. Listeleme öğelerinizin içerdiği diğer bilgiler için alanların yanı sıra birincil anahtar olarak id alanını ekleyin.

@Entity(tableName = "users")
data class User(val id: String, val label: String)

Ayrıca, Accessing data using Room DAOs (Room DAO'larını kullanarak verilere erişme) bölümünde açıklandığı gibi bu Room öğesi için bir veri erişimi nesnesi (DAO) tanımlamanız gerekir. Liste öğesi varlığı için DAO, aşağıdaki yöntemleri içermelidir:

  • Bir öğe listesini tabloya ekleyen insertAll() yöntemi.
  • Sorgu dizesini parametre olarak alan ve sonuç listesi için bir PagingSource nesnesi döndüren bir yöntem. Bu şekilde, bir Pager nesnesi bu tabloyu sayfalandırılmış veri kaynağı olarak kullanabilir.
  • Tablonun tüm verilerini silen bir clearAll() yöntemi.
@Dao
interface UserDao {
  @Insert(onConflict = OnConflictStrategy.REPLACE)
  suspend fun insertAll(users: List<User>)

  @Query("SELECT * FROM users WHERE label LIKE :query")
  fun pagingSource(query: String): PagingSource<Int, User>

  @Query("DELETE FROM users")
  suspend fun clearAll()
}

RemoteMediator uygulama

RemoteMediator'nın temel görevi, Pager'nın verisi bittiğinde veya mevcut veriler geçersiz kılındığında ağdan daha fazla veri yüklemektir. Yükleme davranışını tanımlamak için geçersiz kılmanız gereken bir load() yöntemi içerir.

Tipik bir RemoteMediator uygulaması aşağıdaki parametreleri içerir:

  • query: Arka uç hizmetinden hangi verilerin alınacağını tanımlayan bir sorgu dizesi.
  • database: Yerel önbellek olarak kullanılan Room veritabanı.
  • networkService: Arka uç hizmeti için bir API örneği.

RemoteMediator<Key, Value> uygulaması oluşturun. Key türü ve Value türü, aynı ağ veri kaynağına karşı bir PagingSource tanımlıyormuşsunuz gibi aynı olmalıdır. Tür parametrelerini seçme hakkında daha fazla bilgi için Anahtar ve değer türlerini seçme başlıklı makaleyi inceleyin.

@OptIn(ExperimentalPagingApi::class)
class ExampleRemoteMediator(
  private val query: String,
  private val database: RoomDb,
  private val networkService: ExampleBackendService
) : RemoteMediator<Int, User>() {
  val userDao = database.userDao()

  override suspend fun load(
    loadType: LoadType,
    state: PagingState<Int, User>
  ): MediatorResult {
    // ...
  }
}

load() yöntemi, destekleyen veri kümesini güncellemekten ve PagingSource öğesini geçersiz kılmaktan sorumludur. Sayfalandırmayı destekleyen bazı kitaplıklar (ör. Room), uyguladıkları PagingSource nesnelerinin geçersiz kılınmasını otomatik olarak işler.

load() yöntemi iki parametre alır:

load() yönteminin döndürdüğü değer bir MediatorResult nesnesidir. MediatorResult, MediatorResult.Error (hata açıklamasını içerir) veya MediatorResult.Success (yüklenecek daha fazla veri olup olmadığını belirten bir sinyali içerir) olabilir.

load() yöntemi aşağıdaki adımları gerçekleştirmelidir:

  1. Yükleme türüne ve şu ana kadar yüklenen verilere bağlı olarak ağdan yüklenecek sayfayı belirleyin.
  2. Ağ isteğini tetikleyin.
  3. Yükleme işleminin sonucuna bağlı olarak aşağıdaki işlemleri yapın:
    • Yükleme başarılı olursa ve alınan öğe listesi boş değilse liste öğelerini veritabanında saklayın ve MediatorResult.Success(endOfPaginationReached = false) değerini döndürün. Veriler depolandıktan sonra, Paging kitaplığına yeni verileri bildirmek için veri kaynağını geçersiz kılın.
    • Yükleme başarılıysa ve alınan öğe listesi boşsa veya son sayfa diziniyse MediatorResult.Success(endOfPaginationReached = true) değerini döndürün. Veriler depolandıktan sonra, yeni veriler hakkında Paging kitaplığını bilgilendirmek için veri kaynağını geçersiz kılın.
    • İstek hataya neden olursa MediatorResult.Error değerini döndürün.
override suspend fun load(
  loadType: LoadType,
  state: PagingState<Int, User>
): MediatorResult {
  return try {
    // The network load method takes an optional after=<user.id>
    // parameter. For every page after the first, pass the last user
    // ID to let it continue from where it left off. For REFRESH,
    // pass null to load the first page.
    val loadKey = when (loadType) {
      LoadType.REFRESH -> null
      // In this example, you never need to prepend, since REFRESH
      // will always load the first page in the list. Immediately
      // return, reporting end of pagination.
      LoadType.PREPEND ->
        return MediatorResult.Success(endOfPaginationReached = true)
      LoadType.APPEND -> {
        val lastItem = state.lastItemOrNull()

        // You must explicitly check if the last item is null when
        // appending, since passing null to networkService is only
        // valid for initial load. If lastItem is null it means no
        // items were loaded after the initial REFRESH and there are
        // no more items to load.
        if (lastItem == null) {
          return MediatorResult.Success(
            endOfPaginationReached = true
          )
        }

        lastItem.id
      }
    }

    // Suspending network load via Retrofit. This doesn't need to be
    // wrapped in a withContext(Dispatcher.IO) { ... } block since
    // Retrofit's Coroutine CallAdapter dispatches on a worker
    // thread.
    val response = networkService.searchUsers(
      query = query, after = loadKey
    )

    database.withTransaction {
      if (loadType == LoadType.REFRESH) {
        userDao.deleteByQuery(query)
      }

      // Insert new users into database, which invalidates the
      // current PagingData, allowing Paging to present the updates
      // in the DB.
      userDao.insertAll(response.users)
    }

    MediatorResult.Success(
      endOfPaginationReached = response.nextKey == null
    )
  } catch (e: IOException) {
    MediatorResult.Error(e)
  } catch (e: HttpException) {
    MediatorResult.Error(e)
  }
}

initialize yöntemini tanımlayın

RemoteMediator uygulamaları, önbelleğe alınmış verilerin güncel olup olmadığını kontrol etmek için initialize() yöntemini de geçersiz kılabilir ve uzaktan yenileme işleminin tetiklenip tetiklenmeyeceğine karar verebilir. Bu yöntem, herhangi bir yükleme işlemi yapılmadan önce çalışır. Böylece, yerel veya uzak yüklemeleri tetiklemeden önce veritabanını değiştirebilirsiniz (örneğin, eski verileri temizlemek için).

initialize() işlevi eşzamansız olduğundan, veritabanındaki mevcut verilerin alaka düzeyini belirlemek için veri yükleyebilirsiniz. En yaygın durum, önbelleğe alınan verilerin yalnızca belirli bir süre için geçerli olmasıdır. RemoteMediator, bu son kullanma süresinin geçip geçmediğini kontrol edebilir. Bu durumda Paging kitaplığının verileri tamamen yenilemesi gerekir. initialize() uygulamaları, aşağıdaki şekilde bir InitializeAction döndürmelidir:

  • Yerel verilerin tamamen yenilenmesi gerektiği durumlarda initialize(), InitializeAction.LAUNCH_INITIAL_REFRESH değerini döndürmelidir. Bu işlem, RemoteMediator tarayıcısının verileri tamamen yeniden yüklemek için uzaktan yenileme yapmasına neden olur. Tüm uzak APPEND veya PREPEND yüklemeleri, devam etmeden önce REFRESH yüklemesinin başarılı olmasını bekler.
  • Yerel verilerin yenilenmesi gerekmediği durumlarda initialize(), InitializeAction.SKIP_INITIAL_REFRESH değerini döndürmelidir. Bu durumda RemoteMediator, uzaktan yenileme işlemini atlar ve önbelleğe alınmış verileri yükler.
override suspend fun initialize(): InitializeAction {
  val cacheTimeout = TimeUnit.MILLISECONDS.convert(1, TimeUnit.HOURS)
  return if (System.currentTimeMillis() - db.lastUpdated() <= cacheTimeout)
  {
    // Cached data is up-to-date, so there is no need to re-fetch
    // from the network.
    InitializeAction.SKIP_INITIAL_REFRESH
  } else {
    // Need to refresh cached data from network; returning
    // LAUNCH_INITIAL_REFRESH here will also block RemoteMediator's
    // APPEND and PREPEND from running until REFRESH succeeds.
    InitializeAction.LAUNCH_INITIAL_REFRESH
  }
}

Çağrı cihazı oluşturma

Son olarak, sayfalandırılmış veri akışını ayarlamak için bir Pager örneği oluşturmanız gerekir. Bu, basit bir ağ veri kaynağından Pager oluşturmaya benzer ancak iki şeyi farklı yapmanız gerekir:

  • PagingSource oluşturucusunu doğrudan iletmek yerine, DAO'dan PagingSource nesnesi döndüren sorgu yöntemini sağlamanız gerekir.
  • RemoteMediator uygulamanızın bir örneğini remoteMediator parametresi olarak sağlamalısınız.
val userDao = database.userDao()
val pager = Pager(
  config = PagingConfig(pageSize = 50)
  remoteMediator = ExampleRemoteMediator(query, database, networkService)
) {
  userDao.pagingSource(query)
}

Yarışma durumlarını işleme

Uygulamanızın birden fazla kaynaktan veri yüklerken ele alması gereken durumlardan biri, yerel olarak önbelleğe alınan verilerin uzak veri kaynağıyla senkronize olmamasıdır.

RemoteMediator uygulamanızdaki initialize() yöntemi LAUNCH_INITIAL_REFRESH değerini döndürdüğünde veriler güncel değildir ve yeni verilerle değiştirilmesi gerekir. Tüm PREPEND veya APPEND yükleme istekleri, uzak REFRESH yükleme işleminin başarılı olmasını beklemek zorunda kalır. PREPEND veya APPEND istekleri, REFRESH isteğinden önce sıraya alındığından, bu yükleme çağrılarına iletilen PagingState değerlerinin çalıştırıldıkları sırada güncel olmaması mümkündür.

Verilerin yerel olarak nasıl depolandığına bağlı olarak, önbelleğe alınan verilerdeki değişiklikler geçersiz kılmaya ve yeni veri getirme işlemlerine neden olursa uygulamanız gereksiz istekleri yoksayabilir. Örneğin, oda, herhangi bir veri ekleme işleminde sorguları geçersiz kılar. Bu, veritabanına yeni veriler eklendiğinde, yenilenen verileri içeren yeni PagingSource nesnelerinin bekleyen yükleme isteklerine sağlandığı anlamına gelir.

Kullanıcıların en alakalı ve güncel verileri görmesini sağlamak için bu veri senkronizasyonu sorununun çözülmesi gerekir. En iyi çözüm, büyük ölçüde ağ veri kaynağı sayfalarının verileri nasıl sayfaladığına bağlıdır. Her durumda, uzak anahtarlar, sunucudan istenen en son sayfayla ilgili bilgileri kaydetmenize olanak tanır. Uygulamanız, bir sonraki yüklenecek doğru veri sayfasını belirlemek ve istemek için bu bilgileri kullanabilir.

Uzaktan kumanda anahtarlarını yönetme

Uzak anahtarlar, bir RemoteMediator uygulamasının arka uç hizmetine hangi verilerin bir sonraki aşamada yükleneceğini bildirmek için kullandığı anahtarlardır. En basit durumda, sayfalandırılmış verilerin her öğesi, kolayca başvurabileceğiniz bir uzak anahtar içerir. Ancak uzaktan anahtarlar tek tek öğelere karşılık gelmiyorsa bunları ayrı ayrı saklamanız ve load() yönteminizde yönetmeniz gerekir.

Bu bölümde, tek tek öğelerde depolanmayan uzaktan kumanda anahtarlarının nasıl toplanacağı, depolanacağı ve güncelleneceği açıklanmaktadır.

Öğe anahtarları

Bu bölümde, tek tek öğelere karşılık gelen uzak anahtarlarla nasıl çalışılacağı açıklanmaktadır. Genellikle, bir API bağımsız öğelerden ayrıldığında öğe kimliği bir sorgu parametresi olarak iletilir. Parametre adı, sunucunun sağlanan kimlikten önceki veya sonraki öğelerle yanıt verip vermeyeceğini gösterir. User model sınıfı örneğinde, ek veri istenirken sunucudaki id alanı uzak anahtar olarak kullanılır.

load() yönteminizin öğeye özgü uzak anahtarları yönetmesi gerektiğinde bu anahtarlar genellikle sunucudan getirilen verilerin kimlikleridir. Yenileme işlemleri yalnızca en son verileri aldığından yükleme anahtarı gerektirmez. Benzer şekilde, yenileme işlemi her zaman sunucudan en yeni verileri çektiği için ekleme işlemlerinin herhangi bir ek veri getirmesi gerekmez.

Ancak ekleme işlemleri için kimlik gerekir. Bu işlem için veritabanındaki son öğeyi yüklemeniz ve sonraki veri sayfasını yüklemek için bu öğenin kimliğini kullanmanız gerekir. Veritabanında öğe yoksa endOfPaginationReached değeri doğru (true) olarak ayarlanır. Bu, verilerin yenilenmesi gerektiğini gösterir.

@OptIn(ExperimentalPagingApi::class)
class ExampleRemoteMediator(
  private val query: String,
  private val database: RoomDb,
  private val networkService: ExampleBackendService
) : RemoteMediator<Int, User>() {
  val userDao = database.userDao()

  override suspend fun load(
    loadType: LoadType,
    state: PagingState<Int, User>
  ): MediatorResult {
    return try {
      // The network load method takes an optional String
      // parameter. For every page after the first, pass the String
      // token returned from the previous page to let it continue
      // from where it left off. For REFRESH, pass null to load the
      // first page.
      val loadKey = when (loadType) {
        LoadType.REFRESH -> null
        // In this example, you never need to prepend, since REFRESH
        // will always load the first page in the list. Immediately
        // return, reporting end of pagination.
        LoadType.PREPEND -> return MediatorResult.Success(
          endOfPaginationReached = true
        )
        // Get the last User object id for the next RemoteKey.
        LoadType.APPEND -> {
          val lastItem = state.lastItemOrNull()

          // You must explicitly check if the last item is null when
          // appending, since passing null to networkService is only
          // valid for initial load. If lastItem is null it means no
          // items were loaded after the initial REFRESH and there are
          // no more items to load.
          if (lastItem == null) {
            return MediatorResult.Success(
              endOfPaginationReached = true
            )
          }

          lastItem.id
        }
      }

      // Suspending network load via Retrofit. This doesn't need to
      // be wrapped in a withContext(Dispatcher.IO) { ... } block
      // since Retrofit's Coroutine CallAdapter dispatches on a
      // worker thread.
      val response = networkService.searchUsers(query, loadKey)

      // Store loaded data, and next key in transaction, so that
      // they're always consistent.
      database.withTransaction {
        if (loadType == LoadType.REFRESH) {
          userDao.deleteByQuery(query)
        }

        // Insert new users into database, which invalidates the
        // current PagingData, allowing Paging to present the updates
        // in the DB.
        userDao.insertAll(response.users)
      }

      // End of pagination has been reached if no users are returned from the
      // service
      MediatorResult.Success(
        endOfPaginationReached = response.users.isEmpty()
      )
    } catch (e: IOException) {
      MediatorResult.Error(e)
    } catch (e: HttpException) {
      MediatorResult.Error(e)
    }
  }
}

Sayfa tuşları

Bu bölümde, tek tek öğelere karşılık gelmeyen uzak anahtarlarla nasıl çalışılacağı açıklanmaktadır.

Uzak anahtar tablosu ekleme

Uzak anahtarlar liste öğeleriyle doğrudan ilişkilendirilmediğinde, bunları yerel veritabanındaki ayrı bir tabloda saklamak en iyisidir. Uzak anahtarlar tablosunu temsil eden bir Varlık tanımlayın:

@Entity(tableName = "remote_keys")
data class RemoteKey(val label: String, val nextKey: String?)

Ayrıca RemoteKey öğesi için bir DAO tanımlamanız gerekir:

@Dao
interface RemoteKeyDao {
  @Insert(onConflict = OnConflictStrategy.REPLACE)
  suspend fun insertOrReplace(remoteKey: RemoteKey)

  @Query("SELECT * FROM remote_keys WHERE label = :query")
  suspend fun remoteKeyByQuery(query: String): RemoteKey

  @Query("DELETE FROM remote_keys WHERE label = :query")
  suspend fun deleteByQuery(query: String)
}

Uzaktan anahtarlarla yükleme

load() yönteminizin uzak sayfa anahtarlarını yönetmesi gerektiğinde, RemoteMediator'ün temel kullanımına kıyasla yöntemi aşağıdaki şekillerde farklı tanımlamanız gerekir:

  • Uzak anahtar tablonuz için DAO'ya referans içeren ek bir özellik ekleyin.
  • PagingState kullanmak yerine uzak anahtar tablosunu sorgulayarak bir sonraki anahtarın hangisi olacağını belirleyin.
  • Ağ veri kaynağından döndürülen uzak anahtarı, sayfalandırılmış verilerin kendisinin yanı sıra ekleyin veya depolayın.
@OptIn(ExperimentalPagingApi::class)
class ExampleRemoteMediator(
  private val query: String,
  private val database: RoomDb,
  private val networkService: ExampleBackendService
) : RemoteMediator<Int, User>() {
  val userDao = database.userDao()
  val remoteKeyDao = database.remoteKeyDao()

  override suspend fun load(
    loadType: LoadType,
    state: PagingState<Int, User>
  ): MediatorResult {
    return try {
      // The network load method takes an optional String
      // parameter. For every page after the first, pass the String
      // token returned from the previous page to let it continue
      // from where it left off. For REFRESH, pass null to load the
      // first page.
      val loadKey = when (loadType) {
        LoadType.REFRESH -> null
        // In this example, you never need to prepend, since REFRESH
        // will always load the first page in the list. Immediately
        // return, reporting end of pagination.
        LoadType.PREPEND -> return MediatorResult.Success(
          endOfPaginationReached = true
        )
        // Query remoteKeyDao for the next RemoteKey.
        LoadType.APPEND -> {
          val remoteKey = database.withTransaction {
            remoteKeyDao.remoteKeyByQuery(query)
          }

          // You must explicitly check if the page key is null when
          // appending, since null is only valid for initial load.
          // If you receive null for APPEND, that means you have
          // reached the end of pagination and there are no more
          // items to load.
          if (remoteKey.nextKey == null) {
            return MediatorResult.Success(
              endOfPaginationReached = true
            )
          }

          remoteKey.nextKey
        }
      }

      // Suspending network load via Retrofit. This doesn't need to
      // be wrapped in a withContext(Dispatcher.IO) { ... } block
      // since Retrofit's Coroutine CallAdapter dispatches on a
      // worker thread.
      val response = networkService.searchUsers(query, loadKey)

      // Store loaded data, and next key in transaction, so that
      // they're always consistent.
      database.withTransaction {
        if (loadType == LoadType.REFRESH) {
          remoteKeyDao.deleteByQuery(query)
          userDao.deleteByQuery(query)
        }

        // Update RemoteKey for this query.
        remoteKeyDao.insertOrReplace(
          RemoteKey(query, response.nextKey)
        )

        // Insert new users into database, which invalidates the
        // current PagingData, allowing Paging to present the updates
        // in the DB.
        userDao.insertAll(response.users)
      }

      MediatorResult.Success(
        endOfPaginationReached = response.nextKey == null
      )
    } catch (e: IOException) {
      MediatorResult.Error(e)
    } catch (e: HttpException) {
      MediatorResult.Error(e)
    }
  }
}

Yerinde yenileme

Uygulamanızın yalnızca önceki örneklerde olduğu gibi listenin en üstünden ağ yenilemelerini desteklemesi gerekiyorsa RemoteMediator, yükleme davranışını tanımlaması gerekmez.

Ancak uygulamanızın ağdan yerel veritabanına artımlı olarak yüklemeyi desteklemesi gerekiyorsa kullanıcının kaydırma konumu olan sabitten başlayarak sayfalara ayırma işlemini sürdürme desteği sağlamanız gerekir. Room'un PagingSource uygulaması bunu sizin için halleder ancak Room kullanmıyorsanız PagingSource.getRefreshKey()'ı geçersiz kılarak bunu yapabilirsiniz. getRefreshKey() için örnek bir uygulama görmek istiyorsanız Define the PagingSource başlıklı makaleyi inceleyin.

Şekil 2, önce yerel veritabanından, ardından veritabanında veri kalmadığında ağdan veri yükleme sürecini gösterir.

PagingSource, veritabanında veri kalmayana kadar veritabanından kullanıcı arayüzüne yükleme yapar. Ardından RemoteMediator, ağdan veritabanına yüklenir ve PagingSource yüklemeye devam eder.
Şekil 2. PagingSource ve RemoteMediator'ın verileri yüklemek için birlikte nasıl çalıştığını gösteren diyagram.

Ek kaynaklar

Paging kitaplığı hakkında daha fazla bilgi edinmek için aşağıdaki ek kaynaklara bakın:

İçeriği görüntüleme