Android'de REST uygulamaları için en iyi RxJava operatörleri

Standart RxJava paketinde birçok farklı operatör vardır. Bazıları kullanımı oldukça sağlam ve karmaşık, bazıları ise oldukça basit. Ancak birçok RxJava operatörünün ortak özelliği olan bir şey var:

Asla kullanmayacağın çoğu

Her şeyi RxJava'da yapan her gün Android geliştiricisi olarak, birçok kez zip () operatörünü kullanmaya çalıştım ve her seferinde başaramadım. Her zaman ondan daha iyi bir şey buldum, ya da bu operatörün kapsamayacağı bir durum. Zip () 'in hiç bir kullanımının olmadığını söylemiyorum, birisi beğenebilir ve bu sizin için işe yararsa - Bu harika. Ancak, süper yararlı bulduğum bazı operatörleri tartışalım ve REST tabanlı uygulamada kullanımı harika ve kolay.

Ve işte onlar:

  • Paylaş()
  • tekrar (1) .refCount ()
Ne yaptıklarını zaten biliyorsanız, benim için bir alkış bırakıp bu noktada okumayı bitirebilirsiniz.

Sıcak ya da soğuk ?

Sıklıkla anlaşılması zor olan en önemli şeylerden biri, gözlenebilirin Sıcak mı yoksa Soğuk mu olduğudur. Bunu anlatan birçok harika makale var ve tekrar yapmaya niyetim yok, bunun yerine pratikte nasıl çalıştığına dair örnekler göstereceğim.

Sonuçta, aramanızın gözlemlenebilir Sıcak, Soğuk veya Sıcak olması önemli mi?

Hayır.

Tek önemli olan şudur: işi yaparsa.

Genel olarak iki tür gözlemlenebilirliğe ihtiyaç duyabilirsiniz:

  • son yayılan değeri hatırladığını ve tüm yeni abonelere yaydığını gözlemlenebilir,
  • gözlemlenebilir, bunun son yayılan değerini hatırlamıyor.

Konuşma ucuz. Bana kodu göster.

Diyelim ki uygulamamızda bazı verileri indirmek ve görüntülemek istiyoruz. Bunu yapmanın en kolay yolunu hayal edelim:

users usersObservable = service.getUsers ()
         .subscribeOn (networkScheduler)
         .observeOn (UiScheduler)
         .subscribe {view.update (it)}
         .subscribe {view.update (it)}

Orada. Şimdi hata işleme ekleyelim:

users usersObservable = service.getUsers ()
         .subscribeOn (networkScheduler)
         .observeOn (UiScheduler)
usersObservable
         .filter {it.isNotError ()}
         .subscribe {view.update (it)}
usersObservable
         .filter {it.isError ()}
         .subscribe {view.showErrorMessage ()}

Harika. Ayrıca, en iyi UX için bir ilerleme etkinliği ve boş bir liste ekleyelim:

users usersObservable = service.getUsers ()
         .subscribeOn (networkScheduler)
         .observeOn (UiScheduler)
usersObservable
         .filter {it.isNotError ()}
         .subscribe {view.update (it)}
usersObservable
         .filter {it.isError ()}
         .subscribe {view.showErrorMessage ()}
usersObservable
         .map (yanlış)
         .startWith (doğru)
         .subscribe {progressLoading.visibility = it}
usersObservable
         .map (it.isEmpty ())
         .startWith (yanlış)
         .subscribe {emptyMessage.visibility = it}

Şimdi… bu kodda bir sorun mu var? Test edebiliriz.

@Ölçek
eğlence testi () {
    val usersOrError = Observable.just (listOf ("user1", "user2"))
            .mergeWith (Observable.never ())
            .doOnNext {println (it)}

    usersOrError.subscribe ()
    usersOrError.subscribe ()
    usersOrError.subscribe ()
    usersOrError.subscribe ()

}

Yukarıdaki testte, bir REST isteği yerineObservable.just () var. Neden birleşelim (asla ())? Çünkü, her abone kendisine abone olma şansına sahip olmadan önce gözlemlenebilmemizi istemiyoruz. Bazı durumlar kullanıcı tıklama girişi tarafından tetiklendiğinde benzer durum (hiç bitmeyen gözlemlenebilir) fark edilebilir. Bu dava daha sonra makalede ele alınacaktır. Ayrıca önceki örnekte kullanılan dört gözlemci yalnızca abone () için basitleştirildi. Zamanlayıcılar bölümünü görmezden gelebiliriz, çünkü her şey bir iş parçacığında olur. Nihai sonuç:

[kullanıcı1, kullanıcı2]
[kullanıcı1, kullanıcı2]
[kullanıcı1, kullanıcı2]
[kullanıcı1, kullanıcı2]

Kullanıcıların veya gözlenebilir her abonelik println () 'i tetikledi, bu da gerçek hayattaki uygulamada bir yerine dört istek başlattığımız anlamına geliyor. Bu çok tehlikeli bir durum olabilir. Potansiyel olarak zararsız GET isteği yerine POST yapacağımızı veya verilerin veya uygulamanın durumunu değiştiren başka bir yöntem arayacağımızı hayal edin. Aynı talep dört kez gerçekleştirilecek ve örneğin dört özdeş mesaj veya yorum oluşturulacak.

Neyse ki, tekrar (1) .refCount () ekleyerek kolayca düzeltebiliriz.

@Ölçek
eğlenceli `test tekrarı refCount operatörleri` () {
    val usersOrError = Observable.just (listOf ("user1", "user2"))
            .mergeWith (Observable.never ())
            .doOnNext {println (it)}
            .replay (1)
            .refCount ()

    usersOrError.subscribe ()
    usersOrError.subscribe ()
    usersOrError.subscribe ()
    usersOrError.subscribe ()

}

Bu testin sonucu:

[kullanıcı1, kullanıcı2]

Harika, aboneliğimizi tüm aboneler arasında başarıyla paylaştık. Artık gereksiz çoklu talepte bulunma tehdidi yok. Aynı şeyi replay (1) .refCount () yerine share () operatörü ile gözlemlenebilir şekilde deneyelim.

@Ölçek
fun `test paylaşım operatörü` () {
    val usersOrError = Observable.just (listOf ("user1", "user2"))
            .mergeWith (Observable.never ())
            .doOnNext {println (it)}
            .Paylaş()

    usersOrError.subscribe ()
    usersOrError.subscribe ()
    usersOrError.subscribe ()
    usersOrError.subscribe ()

}

Şaşırtıcı şekilde (ya da değil) sonuç önceki ile aynıdır:

[kullanıcı1, kullanıcı2]

Share () ve replay (1) .refCount () arasındaki farka tanık olmak için iki test daha yapalım. Bu kez kullanıcıdan click olayını aldıktan sonra sahte isteğimizi arayacağız. Tıklama olayı aPublishSubject ile alay edilecek. Ek satır: doOnNext {println ("1")} hangi abonenin etkinliği kullanıcılardan aldığını gösterirOrError

İlk test payı () ve ikincisi tekrarı (1) .refCount kullanacak.

@Ölçek
fun` ile test paylaşımı operatörü`` () {
    val clickEvent = PublishSubject.create  ()

    val usersOrError = clickEvent
            .flatMap {Observable.just (listOf ("user1", "user2"))}
            .Paylaş()

    usersOrError.doOnNext {println ("1")} .subscribe ()
    usersOrError.doOnNext {println ("2")} .subscribe ()

    clickEvent.onNext (Any ()) // gerçekleştirme tıklaması

    usersOrError.doOnNext {println ("3")} .subscribe ()
    usersOrError.doOnNext {println ("4")} .subscribe ()

}

Sonuç:

1
2

@Ölçek
eğlenceli `test tekrarı refCount operatörlerini click` () ile
    val clickEvent = PublishSubject.create  ()

    val usersOrError = clickEvent
            .flatMap {Observable.just (listOf ("user1", "user2"))}
            .replay (1)
            .refCount ()

    usersOrError.doOnNext {println ("1")} .subscribe ()
    usersOrError.doOnNext {println ("2")} .subscribe ()

    clickEvent.onNext (Any ()) // gerçekleştirme tıklaması

    usersOrError.doOnNext {println ("3")} .subscribe ()
    usersOrError.doOnNext {println ("4")} .subscribe ()

}

Sonuç:

1
2
3
4

Sonuç

Hem share () hem de replay (1) .refCount (), REST isteklerini yerine getirmek için önemli operatörler ve çok daha fazlası. Aynı yerde birden fazla yerde gözlemlenebilirliğe ihtiyacınız olduğunda, gitmek için en iyi yol budur. Sadece, gözlemlenebilirinizin en son etkinliği hatırlamasını ve her bir yeni aboneye iletmesini istiyorsanız ya da sadece bir defalık bir işlemle ilgilendiğinizi düşünüyorsanız. İşte bazı gerçek hayattan uygulama örnekleri:

  • getUsers (), getPosts () veya veri elde etmek için kullanılan benzer gözlemlenebilirler büyük olasılıkla usereplay (1) .refCount (),
  • updateUser (), addComment () ise bir kereye mahsus işlemlerdir ve bu durumda share () daha iyisini yapar,
  • click olayının gözlemlenebilen - RxView.clicks (view) - click olayının her aboneye yayınlanmasını sağlamak için ayrıca () operatörüne sahip olması gerekir.

TL; DR

  • share () -> tüm abonelere gözlemlenebilir olanı paylaşır, yeni abonelere son değer vermez
  • replay (1) .refCount () -> tüm abonelere gözlemlenebilir olanı paylaşır ve her yeni aboneye en son değeri verir

Çalışmalarımı seviyorsanız, ❤ düğmesine basın ve yorumlarda ne düşündüğünüzü bana bildirin.