Toán tử RxJava tốt nhất cho các ứng dụng REST trong Android

Có nhiều toán tử khác nhau trong gói RxJava tiêu chuẩn. Một số trong số chúng thực sự mạnh mẽ và phức tạp để sử dụng, những cái khác khá đơn giản. Nhưng có một điều mà nhiều nhà khai thác RxJava có điểm chung:

Hầu hết trong số họ bạn sẽ không bao giờ sử dụng

Là nhà phát triển Android hàng ngày, người thực hiện tất cả mọi thứ trong RxJava, nhiều lần tôi đã cố gắng sử dụng toán tử zip () và mỗi lần tôi không thực hiện được. Tôi đã luôn luôn tìm thấy một cái gì đó tốt hơn nó, hoặc một tình huống mà nhà điều hành này sẽ không bao gồm. Tôi không nói rằng zip () hoàn toàn không có cách sử dụng, ai đó có thể thích nó và nếu điều đó hiệu quả với bạn - Điều đó thật tuyệt vời. Nhưng hãy để thảo luận về một số toán tử mà tôi thấy siêu hữu ích và chúng rất dễ sử dụng trong ứng dụng dựa trên REST.

Và họ ở đây:

  • chia sẻ()
  • phát lại (1) .refCount ()
Nếu bạn đã biết những gì họ đang làm, bạn cũng có thể để lại một cái vỗ tay cho tôi và đọc xong vào thời điểm này.

Nóng hay lạnh ?

Một trong những điều quan trọng nhất thường khó hiểu là liệu có thể quan sát được là Nóng hay Lạnh. Đã có nhiều bài viết tuyệt vời giải thích nó và tôi không có ý định làm lại, thay vào đó tôi sẽ cho bạn thấy những ví dụ về cách nó hoạt động trong thực tế.

Rốt cuộc, có vấn đề gì nếu cuộc gọi của bạn một số nóng, lạnh hoặc ấm có thể quan sát được?

Không.

Tất cả vấn đề là: nếu nó làm công việc.

Nói chung, bạn có thể cần hai loại quan sát:

  • có thể quan sát được rằng ghi nhớ giá trị phát ra cuối cùng và phát ra nó cho tất cả những người đăng ký mới,
  • có thể quan sát mà không nhớ giá trị phát ra cuối cùng của nó.

Nói là rẻ. Cho tôi xem mã.

Hãy nói rằng trong ứng dụng của chúng tôi, chúng tôi muốn tải xuống một số dữ liệu và hiển thị nó. Hãy để tưởng tượng cách dễ nhất để làm điều đó:

val usersObservable = service.getUsers ()
         .subscribeOn (networkScheduler)
         .observeOn (UiScheduler)
         .subscribe {view.update (nó)}
         .subscribe {view.update (nó)}

Đó Bây giờ, hãy để thêm vào xử lý lỗi:

val usersObservable = service.getUsers ()
         .subscribeOn (networkScheduler)
         .observeOn (UiScheduler)
người dùng có thể quan sát được
         .filter {it.isNotError ()}
         .subscribe {view.update (nó)}
người dùng có thể quan sát được
         .filter {it.isError ()}
         .subscribe {view.showErrorMessage ()}

Tuyệt quá. Nhưng cũng hãy để LÊ thêm một sự kiện tiến độ và danh sách trống cho UX tốt nhất:

val usersObservable = service.getUsers ()
         .subscribeOn (networkScheduler)
         .observeOn (UiScheduler)
người dùng có thể quan sát được
         .filter {it.isNotError ()}
         .subscribe {view.update (nó)}
người dùng có thể quan sát được
         .filter {it.isError ()}
         .subscribe {view.showErrorMessage ()}
người dùng có thể quan sát được
         .map (sai)
         .startWith (đúng)
         .subscribe {ProgressLoading.visibility = it}
người dùng có thể quan sát được
         .map (it.isEmpty ())
         .startWith (sai)
         .subscribe {blankMessage.visibility = it}

Bây giờ, có gì sai trong mã này? Chúng tôi có thể kiểm tra nó.

@Kiểm tra
kiểm tra thú vị () {
    val usersOrError = Observable.just (listOf ("user1", "user2"))
            .mergeWith (Có thể quan sát.never ())
            .doOnNext {println (nó)}

    người dùngOrError.subscribe ()
    người dùngOrError.subscribe ()
    người dùngOrError.subscribe ()
    người dùngOrError.subscribe ()

}

Trong thử nghiệm trên, cóObservable.just () thay vì yêu cầu REST. Tại sao hợp nhất với (không bao giờ ())? Bởi vì chúng tôi không muốn có thể quan sát được trước khi mỗi người đăng ký có cơ hội đăng ký. Tình huống tương tự (không bao giờ kết thúc có thể quan sát) có thể được nhận thấy khi một số yêu cầu được kích hoạt bởi đầu vào nhấp chuột của người dùng. Trường hợp này sẽ được đề cập sau trong bài viết. Ngoài ra, bốn quan sát được sử dụng trong ví dụ trước được đơn giản hóa để chỉ đăng ký (). Chúng ta có thể bỏ qua phần lập lịch, vì mọi thứ xảy ra trong một luồng. Kết quả cuối cùng là:

[user1, user2]
[user1, user2]
[user1, user2]
[user1, user2]

Mỗi đăng ký cho người dùng có thể quan sát đượcError đã kích hoạt println () có nghĩa là trong ứng dụng thực tế, chúng tôi chỉ kích hoạt bốn yêu cầu thay vì một yêu cầu. Đây có thể là một tình huống rất nguy hiểm. Hãy tưởng tượng nếu thay vì yêu cầu GET vô hại, chúng ta sẽ tạo POST hoặc gọi một số phương thức khác thay đổi trạng thái của dữ liệu hoặc ứng dụng. Yêu cầu tương tự sẽ được thực hiện bốn lần và ví dụ bốn bài đăng hoặc nhận xét giống hệt nhau sẽ được tạo.

May mắn thay, chúng ta có thể dễ dàng sửa nó bằng cách thêm phát lại (1) .refCount ().

@Kiểm tra
vui vẻ `kiểm tra lại các toán tử refCount` () {
    val usersOrError = Observable.just (listOf ("user1", "user2"))
            .mergeWith (Có thể quan sát.never ())
            .doOnNext {println (nó)}
            .replay (1)
            .refCount ()

    người dùngOrError.subscribe ()
    người dùngOrError.subscribe ()
    người dùngOrError.subscribe ()
    người dùngOrError.subscribe ()

}

Kết quả của bài kiểm tra này là:

[user1, user2]

Tuyệt vời, chúng tôi đã chia sẻ thành công đăng ký giữa tất cả các thuê bao. Bây giờ không có mối đe dọa thực hiện nhiều yêu cầu không cần thiết. Hãy thử cùng một toán tử có thể quan sát được với toán tử share () thay vì phát lại (1) .refCount ().

@Kiểm tra
vui vẻ `toán tử chia sẻ thử nghiệm` () {
    val usersOrError = Observable.just (listOf ("user1", "user2"))
            .mergeWith (Có thể quan sát.never ())
            .doOnNext {println (nó)}
            .chia sẻ()

    người dùngOrError.subscribe ()
    người dùngOrError.subscribe ()
    người dùngOrError.subscribe ()
    người dùngOrError.subscribe ()

}

Đáng ngạc nhiên (hoặc không) kết quả vẫn giống như trước:

[user1, user2]

Để chứng kiến ​​sự khác biệt giữa share () và phát lại (1) .refCount (), hãy để thực hiện thêm hai bài kiểm tra. Lần này chúng tôi sẽ gọi yêu cầu giả của chúng tôi sau khi nhận được sự kiện nhấp chuột từ người dùng. Sự kiện click sẽ được chế tạo bởi aPublishSubject. Dòng bổ sung: doOnNext {println ("1")} sẽ hiển thị người đăng ký nào đã nhận sự kiện từ người dùngOrError

Thử nghiệm đầu tiên sẽ sử dụng share () và lần thứ hai phát lại (1) .refCount.

@Kiểm tra
vui vẻ `toán tử chia sẻ thử nghiệm với click` () {
    val clickEvent = PublishSubject.create  ()

    người dùng valOrError = clickEvent
            .flatMap {Observable.just (listOf ("user1", "user2"))}
            .chia sẻ()

    usersOrError.doOnNext {println ("1")} .subscribe ()
    người dùngOrError.doOnNext {println ("2")} .subscribe ()

    clickEvent.onNext (Any ()) // thực hiện nhấp

    người dùngOrError.doOnNext {println ("3")} .subscribe ()
    usersOrError.doOnNext {println ("4")} .subscribe ()

}

Kết quả:

1
2

@Kiểm tra
vui vẻ `kiểm tra lại các toán tử refCount bằng click` () {
    val clickEvent = PublishSubject.create  ()

    người dùng valOrError = clickEvent
            .flatMap {Observable.just (listOf ("user1", "user2"))}
            .replay (1)
            .refCount ()

    usersOrError.doOnNext {println ("1")} .subscribe ()
    người dùngOrError.doOnNext {println ("2")} .subscribe ()

    clickEvent.onNext (Any ()) // thực hiện nhấp

    người dùngOrError.doOnNext {println ("3")} .subscribe ()
    usersOrError.doOnNext {println ("4")} .subscribe ()

}

Kết quả:

1
2
3
4

Phần kết luận

Cả share () và replay (1) .refCount () đều là các toán tử quan trọng để xử lý các yêu cầu REST và hơn thế nữa. Mỗi khi bạn cần cùng một nơi có thể quan sát được ở nhiều nơi, đó là cách tốt nhất để đi. Chỉ cần nghĩ rằng nếu bạn muốn bạn có thể quan sát được để nhớ sự kiện mới nhất và chuyển nó đến từng người đăng ký mới hoặc có thể bạn quan tâm đến hoạt động chỉ một lần. Dưới đây là một số ví dụ ứng dụng thực tế:

  • getUsers (), getPosts () hoặc tương tự có thể quan sát được sử dụng để lấy dữ liệu rất có thể sẽ là usereplay (1) .refCount (),
  • updateUser (), addVer () mặt khác là các hoạt động chỉ một lần duy nhất và trong trường hợp này share () sẽ làm tốt hơn,
  • sự kiện nhấp qua được gói trong Observable - RxView.clicks (view) - cũng nên có toán tử share (), để chắc chắn rằng sự kiện click sẽ được phát đến mỗi thuê bao.

TL; DR

  • share () -> chia sẻ có thể quan sát được cho tất cả người đăng ký, không phát ra giá trị mới nhất cho người đăng ký mới
  • phát lại (1) .refCount () -> chia sẻ có thể quan sát được cho tất cả người đăng ký và phát ra giá trị mới nhất cho mọi người đăng ký mới

Nếu bạn thích công việc của tôi nhấn nút and và cho tôi biết những gì bạn nghĩ trong các bình luận.