Çevrimdışı öncelikli uygulamalar, internete erişim olmadan temel işlevlerinin tamamını veya önemli bir alt kümesini gerçekleştirebilen bir uygulamadır. Yani iş mantığının bir kısmını veya tamamını çevrimdışı olarak gerçekleştirebilir.
Uygulama verilerine ve iş mantığına erişim sunan veri katmanında çevrimdışı öncelikli bir uygulama başlangıcı oluştururken göz önünde bulundurulması gereken noktalar. Uygulamanın bu verileri zaman zaman cihazın dışındaki kaynaklardan yenilemesi gerekebilir. Bu sırada, güncel kalmak için ağ kaynaklarını çağırması gerekebilir.
Ağ kullanılabilirliği her zaman garanti edilmez. Cihazlarda genellikle ağ bağlantısının bağlantılı veya yavaş olduğu dönemler görülür. Kullanıcılar aşağıdakilerle karşılaşabilir:
- Sınırlı internet bant genişliği
- Asansörde veya tünelde olduğu gibi geçiş sırasında yaşanan bağlantı kesintileri.
- Ara sıra veri erişimi. Örneğin, yalnızca kablosuz ağ kullanan tabletler.
Nedeni ne olursa olsun, bu koşullarda bir uygulamanın düzgün çalışması genellikle mümkündür. Uygulamanızın çevrimdışıyken doğru bir şekilde çalıştığından emin olmak için aşağıdakileri yapabilmesi gerekir:
- Güvenilir bir ağ bağlantısı olmadan kullanılabilir durumda kalın.
- İlk ağ aramasının tamamlanmasını veya başarısız olmasını beklemek yerine, kullanıcılara yerel verileri hemen sunun.
- Verileri, pil ve veri durumuna dikkat ederek getirin. Örneğin, yalnızca şarj veya kablosuz bağlantı gibi optimum koşullarda veri getirme isteğinde bulunabiliriz.
Yukarıdaki ölçütleri karşılayabilen uygulamalara genellikle çevrimdışı öncelikli uygulama denir.
Çevrimdışı öncelikli bir uygulama tasarlama
Çevrimdışı öncelikli bir uygulama tasarlarken işe veri katmanında ve uygulama verileri üzerinde gerçekleştirebileceğiniz iki ana işleme başlamalısınız:
- Okumalar: Uygulamanın diğer bölümleri tarafından kullanılması için verileri alma (ör. kullanıcıya bilgi görüntüleme).
- Yazmalar: Kullanıcı girişinin daha sonra alınması için kalıcıdır.
Veri katmanındaki depolar, uygulama verisi sağlamak için veri kaynaklarının birleştirilmesinden sorumludur. Çevrimdışı öncelikli bir uygulamada, en kritik görevlerini gerçekleştirmek için ağ erişimine ihtiyaç duymayan en az bir veri kaynağı olmalıdır. Bu kritik görevlerden biri verileri okumaktır.
Çevrimdışı öncelikli bir uygulamada verileri modelleyin
Çevrimdışı öncelikli bir uygulamada, ağ kaynaklarından yararlanan her depo için en az 2 veri kaynağı bulunur:
- Yerel veri kaynağı
- Ağ veri kaynağı
Yerel veri kaynağı
Yerel veri kaynağı, uygulamanın standart bilgi kaynağıdır. Uygulamanın üst katmanlarının okuduğu tüm verilerin tek kaynağı olmalıdır. Bu, bağlantı durumları arasında veri tutarlılığı sağlar. Yerel veri kaynağı genellikle diskte tutulan depolama alanıyla desteklenir. Verileri diskte kalıcı olarak tutmak için yaygın olarak kullanılan bazı araçlar şunlardır:
- Room gibi ilişkisel veritabanları gibi yapılandırılmış veri kaynakları.
- Yapılandırılmamış veri kaynakları. Örneğin, Datastore ile protokol arabelleğe alır.
- Basit dosyalar
Ağ veri kaynağı
Ağ veri kaynağı, uygulamanın gerçek durumudur. Yerel veri kaynağı en iyi şekilde ağ veri kaynağıyla senkronize edilir. Ayrıca geride kalabilir. Bu durumda, internete tekrar bağlandığınızda uygulamanın güncellenmesi gerekir.
Bunun tersine, ağ veri kaynağı, bağlantı geri geldiğinde uygulama veri kaynağını güncelleyene kadar yerel veri kaynağının gerisinde kalabilir. Uygulamanın alan ve kullanıcı arayüzü katmanları, hiçbir zaman ağ katmanıyla doğrudan ilişki kurmamalıdır. Cihazla iletişim kurmak ve yerel veri kaynağını güncellemek için kullanmak, barındırma repository
adlı geliştiricinin sorumluluğundadır.
Kaynakları kullanıma sunma
Yerel veri kaynakları ile ağ veri kaynakları, uygulamanızın bu veri kaynaklarını okuma ve yazma biçimleri açısından temel farklılıklar gösterebilir. Yerel veri kaynağını sorgulamak (SQL sorguları kullanırken olduğu gibi) hızlı ve esnek olabilir. Buna karşılık ağ veri kaynakları yavaş ve kısıtlı olabilir (örneğin, kimliğe göre RESTful kaynaklarına artımlı olarak erişirken). Sonuç olarak, her veri kaynağı genellikle sağladığı verileri kendi temsiline ihtiyaç duyar. Dolayısıyla yerel veri kaynağının ve ağ veri kaynağının kendi modelleri olabilir.
Aşağıdaki dizin yapısı bu kavramı görselleştirmektedir. AuthorEntity
, uygulamanın yerel veritabanından okunan bir yazarı temsil eder. NetworkAuthor
ise ağ üzerinden serileştirilmiş bir yazarın temsilidir:
data/
├─ local/
│ ├─ entities/
│ │ ├─ AuthorEntity
│ ├─ dao/
│ ├─ NiADatabase
├─ network/
│ ├─ NiANetwork
│ ├─ models/
│ │ ├─ NetworkAuthor
├─ model/
│ ├─ Author
├─ repository/
AuthorEntity
ve NetworkAuthor
ile ilgili ayrıntılar şu şekildedir:
/**
* Network representation of [Author]
*/
@Serializable
data class NetworkAuthor(
val id: String,
val name: String,
val imageUrl: String,
val twitter: String,
val mediumPage: String,
val bio: String,
)
/**
* Defines an author for either an [EpisodeEntity] or [NewsResourceEntity].
* It has a many-to-many relationship with both entities
*/
@Entity(tableName = "authors")
data class AuthorEntity(
@PrimaryKey
val id: String,
val name: String,
@ColumnInfo(name = "image_url")
val imageUrl: String,
@ColumnInfo(defaultValue = "")
val twitter: String,
@ColumnInfo(name = "medium_page", defaultValue = "")
val mediumPage: String,
@ColumnInfo(defaultValue = "")
val bio: String,
)
Hem AuthorEntity
hem de NetworkAuthor
öğelerini veri katmanının içinde tutmak ve harici katmanların tüketmesi için üçüncü bir türü açığa çıkarmak iyi bir uygulamadır. Bu, harici katmanları yerel ve ağ veri kaynaklarında, uygulamanın davranışını temelden değiştirmeyen küçük değişikliklerden korur.
Bu, aşağıdaki snippet'te gösterilmektedir:
/**
* External data layer representation of a "Now in Android" Author
*/
data class Author(
val id: String,
val name: String,
val imageUrl: String,
val twitter: String,
val mediumPage: String,
val bio: String,
)
Daha sonra ağ modeli, bunu yerel modele dönüştürmek için bir uzantı yöntemi tanımlayabilir ve yerel model de benzer şekilde bunu aşağıda gösterildiği gibi harici gösterime dönüştürecek bir yönteme sahiptir:
/**
* Converts the network model to the local model for persisting
* by the local data source
*/
fun NetworkAuthor.asEntity() = AuthorEntity(
id = id,
name = name,
imageUrl = imageUrl,
twitter = twitter,
mediumPage = mediumPage,
bio = bio,
)
/**
* Converts the local model to the external model for use
* by layers external to the data layer
*/
fun AuthorEntity.asExternalModel() = Author(
id = id,
name = name,
imageUrl = imageUrl,
twitter = twitter,
mediumPage = mediumPage,
bio = bio,
)
Okumalar
Okumalar, çevrimdışı öncelikli bir uygulamada uygulama verileriyle ilgili temel işlemdir. Bu nedenle, uygulamanızın verileri okuyabildiğinden ve yeni veriler sunulur ulaşmaz bunları gösterebildiğinden emin olmanız gerekir. Bunu yapabilen bir uygulama, gözlemlenebilir türlere sahip okuma API'lerini kullanıma sunduğu için reaktif bir uygulamadır.
Aşağıdaki snippet'te OfflineFirstTopicRepository
, tüm okunan API'leri için Flows
değerini döndürür. Bu şekilde, ağ veri kaynağından güncelleme aldığında okuyucularını güncelleyebilir. Başka bir deyişle, yerel veri kaynağı geçersiz olduğunda OfflineFirstTopicRepository
aktarmasının değişmesine izin verir. Bu nedenle, her OfflineFirstTopicRepository
okuyucusu, uygulamaya ağ bağlantısı geri yüklendiğinde tetiklenebilecek veri değişikliklerini işlemeye hazır olmalıdır. Ayrıca OfflineFirstTopicRepository
, verileri doğrudan yerel veri kaynağından okur. Yalnızca önce yerel veri kaynağını güncelleyerek okuyucularını veri değişiklikleri konusunda bilgilendirebilir.
class OfflineFirstTopicsRepository(
private val topicDao: TopicDao,
private val network: NiaNetworkDataSource,
) : TopicsRepository {
override fun getTopicsStream(): Flow<List<Topic>> =
topicDao.getTopicEntitiesStream()
.map { it.map(TopicEntity::asExternalModel) }
}
Hata işleme stratejileri
Çevrimdışı öncelikli uygulamalarda, meydana gelebilecek veri kaynaklarına bağlı olarak hataları ele almanın benzersiz yolları vardır. Aşağıdaki alt bölümlerde bu stratejiler ana hatlarıyla açıklanmaktadır.
Yerel veri kaynağı
Yerel veri kaynağından okuma sırasında hatalar nadiren görülür. Okuyucuları hatalara karşı korumak için okuyucunun veri topladığı Flows
üzerinde catch
operatörünü kullanın.
catch
operatörünün ViewModel
içinde kullanımı aşağıdaki gibidir:
class AuthorViewModel(
authorsRepository: AuthorsRepository,
...
) : ViewModel() {
private val authorId: String = ...
// Observe author information
private val authorStream: Flow<Author> =
authorsRepository.getAuthorStream(
id = authorId
)
.catch { emit(Author.empty()) }
}
Ağ veri kaynağı
Bir ağ veri kaynağından veri okunurken hatalar oluşursa uygulamanın veri getirmeyi yeniden denemek için buluşsal bir yöntem kullanması gerekir. Yaygın buluşsal yöntemler şunları içerir:
Eksponansiyel geri yükleme
Üstel geri yüklemede uygulama, başarılı olana kadar artan zaman aralıklarıyla ağ veri kaynağından veri okumayı denemeye devam eder veya diğer koşullar verinin durması gerektiğini belirtir.
Uygulamanın geri dönmeye devam edip etmeyeceğini değerlendirme ölçütleri şunlardır:
- Ağ veri kaynağının belirttiği hata türü. Örneğin, bağlantı eksikliği olduğunu gösteren bir hata döndüren ağ çağrılarını yeniden denemeniz gerekir. Buna karşılık, uygun kimlik bilgileri elde edilene kadar yetkilendirilmeyen HTTP isteklerini yeniden denememelisiniz.
- İzin verilen maksimum yeniden deneme sayısı.
Ağ bağlantısı izleme
Bu yaklaşımda okuma istekleri, uygulama ağ veri kaynağına bağlanabileceğinden emin olana kadar sıraya alınır. Bağlantı kurulduktan sonra okuma isteği sıraya alınır, veri okunur ve yerel veri kaynağı güncellenir. Android'de bu sıra, bir Room veritabanıyla korunabilir ve WorkManager kullanılarak kalıcı iş olarak boşaltılabilir.
Yazma işlemleri
Çevrimdışı öncelikli bir uygulamada verileri okumanın önerilen yolu gözlemlenebilir türleri kullanmaktır ancak yazma API'lerinin eşdeğeri, askıya alma işlevleri gibi eşzamansız API'lerdir. Bu, kullanıcı arayüzü iş parçacığının engellenmesini önler ve çevrimdışı öncelikli uygulamalarda yazma işlemleri ağ sınırını aşarken başarısız olabileceği için hataların işlenmesine yardımcı olur.
interface UserDataRepository {
/**
* Updates the bookmarked status for a news resource
*/
suspend fun updateNewsResourceBookmark(newsResourceId: String, bookmarked: Boolean)
}
Yukarıdaki snippet'te, yukarıdaki yöntem askıya alındığında tercih edilen eşzamansız API Coroutines'tir.
Stratejileri yazın
Çevrimdışı öncelikli uygulamalarda veri yazarken göz önünde bulundurulması gereken üç strateji vardır. Hangisini seçeceğiniz, yazılan verinin türüne ve uygulamanın gereksinimlerine bağlıdır:
Yalnızca çevrimiçi yazma işlemleri
Verileri ağ sınırı dışına yazmaya çalışın. Başarılı olursa yerel veri kaynağını güncelleyin. Aksi takdirde, istisna oluşturup uygun şekilde yanıt vermesi için arayana bırakın.
Bu strateji genellikle neredeyse gerçek zamanlı olarak online olması gereken yazma işlemleri için kullanılır. Örneğin, banka havalesi. Yazma işlemleri başarısız olabileceğinden genellikle yazma işleminin başarısız olduğunu kullanıcıya bildirmek veya kullanıcının en başta veri yazmayı denemesini önlemek gerekir. Bu senaryolarda uygulayabileceğiniz bazı stratejiler şunlardır:
- Bir uygulama, veri yazmak için internet erişimi gerektiriyorsa kullanıcıya veri yazma izni veren bir kullanıcı arayüzü sunmamayı tercih edebilir veya en azından uygulamayı devre dışı bırakabilir.
- Kullanıcının kapatamayacağı bir pop-up mesaj veya geçici bir istem kullanarak kullanıcıya çevrimdışı olduğunu bildirebilirsiniz.
Sıraya alınmış yazma işlemleri
Yazmak istediğiniz bir nesne olduğunda bunu bir sıraya ekleyin. Uygulama tekrar online olduğunda üstel geri çekilerek sırayı boşaltmaya devam edin. Android'de çevrimdışı sıranın boşaltılması kalıcı bir iştir ve genellikle WorkManager
'e verilir.
Aşağıdaki durumlarda bu yaklaşım iyi bir seçimdir:
- Verilerin ağa yazılması gerekmez.
- İşlem zamana duyarlı değil.
- İşlem başarısız olursa kullanıcıya bilgi verilmesi şart değildir.
Bu yaklaşımın kullanım alanları arasında analiz etkinlikleri ve günlük kaydı yer alır.
Tembel yazmalar
Önce yerel veri kaynağına yazın, ardından en kısa sürede ağa bildirmek için yazma işlemini sıraya alın. Uygulama tekrar çevrimiçi olduğunda ağ ile yerel veri kaynakları arasında çakışmalar olabileceğinden, bu çok önemli bir nokta değildir. Anlaşmazlık çözümüyle ilgili bir sonraki bölümde daha ayrıntılı bilgi verilmiştir.
Bu yaklaşım, veriler uygulama açısından kritik öneme sahip olduğunda doğru seçimdir. Örneğin, çevrimdışı öncelikli bir yapılacaklar listesi uygulamasında, veri kaybı riskini önlemek için kullanıcının çevrimdışına eklediği tüm görevlerin yerel olarak depolanması önemlidir.
Senkronizasyon ve çakışma çözümü
Çevrimdışı öncelikli bir uygulama, bağlantısını tekrar sağladığında yerel veri kaynağındaki verileri ağ veri kaynağındaki verilerle bağdaştırmalıdır. Bu işleme senkronizasyon denir. Bir uygulamanın ağ veri kaynağıyla senkronize edilebileceği iki temel yol vardır:
- Çekme tabanlı senkronizasyon
- Push tabanlı senkronizasyon
Çekme tabanlı senkronizasyon
Çekme tabanlı senkronizasyonda, uygulama isteğe bağlı olarak en son uygulama verilerini okumak için ağa ulaşır. Bu yaklaşımda yaygın olarak kullanılan sezgisel yöntemler, gezinme tabanlıdır. Uygulama, verileri sadece kullanıcıya sunmadan hemen önce getirir.
Bu yaklaşım, en çok uygulamada ağ bağlantısının kısa ve ara dönemlerle olmasını beklerken en iyi sonucu verir. Bunun nedeni, veri yenilemenin fırsata dayalı olması ve bağlantı olmamasının uzun süre boyunca kullanıcının eski veya boş bir önbelleğe sahip uygulama hedeflerini ziyaret etmeye çalışma olasılığını artırmasıdır.
Sayfa jetonlarının, belirli bir ekran için sonsuz bir kaydırma listesindeki öğeleri getirmek amacıyla kullanıldığı bir uygulama düşünün. Uygulama, daha sonra ağa geçilebilir, verileri yerel veri kaynağında saklayabilir ve ardından bilgileri kullanıcıya sunmak için yerel veri kaynağından okuyabilir. Ağ bağlantısının olmadığı durumlarda depo, yalnızca yerel veri kaynağından veri isteyebilir. Bu, Jetpack Paging Library tarafından RemoteMediator API'si ile kullanılan kalıptır.
class FeedRepository(...) {
fun feedPagingSource(): PagingSource<FeedItem> { ... }
}
class FeedViewModel(
private val repository: FeedRepository
) : ViewModel() {
private val pager = Pager(
config = PagingConfig(
pageSize = NETWORK_PAGE_SIZE,
enablePlaceholders = false
),
remoteMediator = FeedRemoteMediator(...),
pagingSourceFactory = feedRepository::feedPagingSource
)
val feedPagingData = pager.flow
}
Pull tabanlı senkronizasyonun avantajları ve dezavantajları aşağıdaki tabloda özetlenmiştir:
Avantajları | Dezavantajları |
---|---|
Uygulaması nispeten kolaydır. | Yoğun veri kullanımına eğilimli. Bunun nedeni, bir gezinme hedefine yapılan tekrarlanan ziyaretlerin, değiştirilmemiş bilgilerin gereksiz bir şekilde yeniden getirilmesini tetiklemesidir. Doğru şekilde önbelleğe alma yoluyla bu sorunu giderebilirsiniz. Bu işlem, kullanıcı arayüzü katmanında cachedIn operatörüyle veya HTTP önbelleğine sahip ağ katmanında yapılabilir. |
İhtiyacınız olmayan veriler hiçbir zaman getirilmez. | Alınan modelin kendi kendine yeterli olması gerektiğinden ilişkisel verilerle iyi ölçeklenemez. Senkronize edilen model, kendisini doldurması için getirilen başka modellere bağlıysa daha önce bahsedilen yoğun veri kullanımı sorunu daha da belirgin hale gelir. Ayrıca, üst modelin depoları ile iç içe yerleştirilmiş modelin depoları arasında bağımlılıklara neden olabilir. |
Push tabanlı senkronizasyon
Aktarma tabanlı senkronizasyonda, yerel veri kaynağı ağ veri kaynağının en iyi şekilde kopya kümesini taklit etmeye çalışır. Sistem, ilk başlatma sırasında proaktif olarak uygun miktarda veri getirerek bir temel oluşturur. Daha sonra, bu veriler eski olduğunda sunucudan gelen bildirimleri kullanır.
Eski bildirimi aldıktan sonra, uygulama yalnızca eski olarak işaretlenmiş verileri güncellemek için ağa ulaşır. Bu çalışma, ağ veri kaynağına ulaşan Repository
ekibine yetki verir ve yerel veri kaynağına getirilen verileri saklar. Depo, verilerini gözlemlenebilir türlerle sunduğundan okuyuculara değişiklikler hakkında bilgi verilir.
class UserDataRepository(...) {
suspend fun synchronize() {
val userData = networkDataSource.fetchUserData()
localDataSource.saveUserData(userData)
}
}
Bu yaklaşımda uygulama, ağ veri kaynağına çok daha az bağımlıdır ve bu veri kaynağı olmadan uzun süre çalışabilir. Yerel olarak ağ veri kaynağından en yeni bilgileri içerdiği varsayıldığı için çevrimdışıyken hem okuma hem de yazma erişimi sunar.
Push tabanlı senkronizasyonun avantajları ve dezavantajları aşağıdaki tabloda özetlenmiştir:
Avantajları | Dezavantajları |
---|---|
Uygulama süresiz olarak çevrimdışı kalabilir. | Çakışma çözümüne yönelik sürüm verilerinin oluşturulması son derece basit bir işlemdir. |
Minimum veri kullanımı. Uygulama yalnızca değiştirilen verileri getirir. | Senkronizasyon sırasında yazmayla ilgili endişeleri göz önünde bulundurmanız gerekir. |
İlişkisel verilerde iyi sonuç verir. Her depo yalnızca desteklediği modele ait verileri getirmekten sorumludur. | Ağ veri kaynağının senkronizasyonu desteklemesi gerekir. |
Karma senkronizasyon
Bazı uygulamalar, verilere bağlı olarak çekme veya aktarma uygulanan karma bir yaklaşım kullanır. Örneğin bir sosyal medya uygulaması, feed güncellemesi sıklığının yüksek olması nedeniyle kullanıcının aşağıdaki feed'ini isteğe bağlı olarak getirmek için çekme tabanlı senkronizasyon kullanabilir. Aynı uygulama, oturum açan kullanıcının kullanıcı adı, profil resmi vb. veriler için push tabanlı senkronizasyonu kullanmayı tercih edebilir.
Sonuç olarak, çevrimdışı öncelikli senkronizasyon tercihi ürün gereksinimlerine ve kullanılabilir teknik altyapıya bağlıdır.
Çatışma çözümü
Uygulama çevrimdışıyken ağ veri kaynağıyla uyuşmayan verileri yerel olarak yazıyorsa senkronizasyonun yapılabilmesi için çözmeniz gereken bir çakışma meydana gelmiştir.
Çatışma çözümü genellikle sürüm oluşturmayı gerektirir. Değişikliklerin ne zaman gerçekleştiğini takip etmek için uygulamanın bir miktar muhasebe yapması gerekir. Bu, meta verilerin ağ veri kaynağına aktarılmasını sağlar. Sonrasında ağ veri kaynağı, mutlak doğru kaynağı sağlama sorumluluğuna sahiptir. Uygulamanın ihtiyaçlarına bağlı olarak çatışmaların çözümü için değerlendirilebilecek çok çeşitli stratejiler vardır. Mobil uygulamalar için yaygın yaklaşım "son yazma kazanır" şeklindedir.
Son yazma kazananları
Bu yaklaşımda cihazlar, ağa yazdıkları verilere zaman damgası meta verileri ekler. Ağ veri kaynağı bunları aldığında geçerli durumundan daha eski verileri siler ve geçerli durumundan daha yeni olanları kabul eder.
Yukarıda, her iki cihaz da çevrimdışıdır ve başlangıçta ağ veri kaynağıyla senkronize edilmiştir. Çevrimdışıyken hem verileri yerel olarak yazar hem de verilerini yazdıkları zamanı takip ederler. İkisi de tekrar çevrimiçi olduğunda ve ağ veri kaynağıyla senkronize edildiğinde, ağ, B cihazındaki verileri daha sonra yazdığından bu verileri kalıcı olarak tutarak çakışmayı çözer.
Çevrimdışı öncelikli uygulamalarda WorkManager
Yukarıda bahsedilen okuma ve yazma stratejilerinin ikisinde yaygın olarak görülen iki yardımcı vardır:
- Sıralar
- Okumalar: Ağ bağlantısı kullanılabilir hale gelene kadar okumaları ertelemek için kullanılır.
- Yazma işlemleri: Ağ bağlantısı kullanılabilir olana kadar yazma işlemlerini ertelemek ve yeniden denemeler için yazma işlemlerini sıraya almak amacıyla kullanılır.
- Ağ bağlantısı izleyicileri
- Okumalar: Uygulama bağlıyken ve senkronizasyon sırasında okuma sırasını boşaltmak için sinyal olarak kullanılır
- Yazma işlemleri: Uygulama bağlıyken yazma sırasını boşaltmak ve senkronizasyon için sinyal olarak kullanılır
Her iki durum da WorkManager'ın uzman olduğu kalıcı iş örnekleridir. Örneğin, Now in Android örnek uygulamasında WorkManager, yerel veri kaynağını senkronize ederken hem okuma sırası hem de ağ izleyicisi olarak kullanılır. Uygulama başlangıçta aşağıdaki işlemleri gerçekleştirir:
- Okuma senkronizasyonunda, yerel veri kaynağı ile ağ veri kaynağı arasında denklik olduğundan emin olmak için sıraya koyun.
- Okuma senkronizasyonu sırasını boşaltın ve uygulama çevrimiçi olduğunda senkronizasyonu başlatın.
- Üstel geri yükleme kullanarak ağ veri kaynağından okuma işlemi gerçekleştirin.
- Oluşabilecek çakışmaları gidermek için okuma işleminin sonuçlarını yerel veri kaynağında kalıcı olarak tutun.
- Yerel veri kaynağındaki verileri, uygulamanın diğer katmanları için de kullanıma sunun.
Yukarıdaki diyagramda gösterilmektedir:
WorkManager ile senkronizasyonu sıraya ekleme işlemi, senkronizasyonu KEEP
ExistingWorkPolicy
ile benzersiz bir çalışma olarak belirtilerek yapılır:
class SyncInitializer : Initializer<Sync> {
override fun create(context: Context): Sync {
WorkManager.getInstance(context).apply {
// Queue sync on app startup and ensure only one
// sync worker runs at any time
enqueueUniqueWork(
SyncWorkName,
ExistingWorkPolicy.KEEP,
SyncWorker.startUpSyncWork()
)
}
return Sync
}
}
Burada SyncWorker.startupSyncWork()
aşağıdaki şekilde tanımlanır:
/**
Create a WorkRequest to call the SyncWorker using a DelegatingWorker.
This allows for dependency injection into the SyncWorker in a different
module than the app module without having to create a custom WorkManager
configuration.
*/
fun startUpSyncWork() = OneTimeWorkRequestBuilder<DelegatingWorker>()
// Run sync as expedited work if the app is able to.
// If not, it runs as regular work.
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.setConstraints(SyncConstraints)
// Delegate to the SyncWorker.
.setInputData(SyncWorker::class.delegatedData())
.build()
val SyncConstraints
get() = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
Özellikle SyncConstraints
ile tanımlanan Constraints
için NetworkType
NetworkType.CONNECTED
gerekir. Yani, çalışmaya başlamadan önce ağ kullanılabilir hale gelene kadar bekler.
Ağ kullanılabilir olduğunda Çalışan, SyncWorkName
tarafından belirlenen benzersiz iş sırasını uygun Repository
örneklerine yetki vererek boşaltır. Senkronizasyon başarısız olursa doWork()
yöntemi, Result.retry()
ile birlikte döndürülür. WorkManager, üstel geri yükleme ile senkronizasyonu otomatik olarak yeniden dener. Aksi takdirde, senkronizasyon tamamlanırken Result.success()
döndürülür.
class SyncWorker(...) : CoroutineWorker(appContext, workerParams), Synchronizer {
override suspend fun doWork(): Result = withContext(ioDispatcher) {
// First sync the repositories in parallel
val syncedSuccessfully = awaitAll(
async { topicRepository.sync() },
async { authorsRepository.sync() },
async { newsRepository.sync() },
).all { it }
if (syncedSuccessfully) Result.success()
else Result.retry()
}
}
Sana Özel
Aşağıdaki Google örneklerinde, çevrimdışına öncelik veren uygulamalar gösterilmektedir. Bu kılavuzu inceleyerek örnekleri inceleyin:
Sizin için önerilenler
- Not: Bağlantı metni JavaScript kapalıyken gösterilir
- Kullanıcı arayüzü durumu üretimi
- Kullanıcı arayüzü katmanı
- Veri katmanı