Is RxRelay magic? Subject vs RxRelay





In the Android community, I've come across three types of developers who have come across RxRelay:



  1. Those who do not understand why RxRelay is used in their project, why it is needed and how it differs from Subject
  2. Those who think that RxRelay "swallows" errors or "after the RxRelay error has occurred, will continue to work, but the Subject will not" (the same magic)
  3. Those who really know what RxRelay is.


While the first two types are more common, I decided to write an article that will help you understand how RxRelay works and check its "magic" properties.



If you are using RxJava, then you probably use Subject or RxRelay to throw events from one entity to another or make reactive code out of imperative code.



Let's check point # 2 and see what is the difference between RxRelay and Subject. So, we have two subscriptions to one relay, when we click on the button, we push one to this relay.



class MainActivity : AppCompatActivity() {
   private val relay = PublishRelay.create<Int>()
   private var isError: Boolean = false

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_main)

       val disposable1 = relay
           .map {
               if (isError) {
                   isError = false
                   throw Exception()
               } else {
                   isError = true
               }
           }.subscribe(
               {
                   Log.d("test", "  : onNext")
               },
               {
                   Log.d("test", "  : onError")
               }
           )

       val disposable2 = relay
           .subscribe(
               {
                   Log.d("test", "  : onNext")
               },
               {
                   Log.d("test", "  : onError")
               }
           )

       btn.setOnClickListener {
           relay.accept(1)
       }
   }
}


We click on the button three times in a row and we see such a log.

D / test: Chain with error: onNext

D / test: Chain without error: onNext



D / test: Chain with error: onError

D / test: Chain without error: onNext



D / test: Chain without error: onNext



If you replace the RxRelay variable with PublishSubject, the log will not change. Here's why:



On the first click, we push data into our relay. Both subscribers are triggered.



On the second click in the chain, the first subscriber (disposable1) gets an error.



On the third click, the first disposable1 no longer fires, since it received the terminal state onError. Then only the second disposable2 will work.



This will be the case with Subject and RxRelay. Let me remind you that in rx, errors go down the chain to the subscriber (downstream) and above the place where they occurred, they do not get. We ended up checking that the RxRelay based chaining could not work after an error occurred.



So if there is no difference in the behavior of Subject and RxRelay, then what is their difference?



Here is what the developer himself writes in the README on github:

β€œBasically: A Subject except without the ability to call onComplete or onError.”



That is, it is just a Subject without onComplete and onError methods, even the source code of the classes is almost the same. If we call these methods on Subject, then it will stop working, since it will receive a terminal state. Therefore, the author of the library decided that it was worth removing these methods, because those developers who do not know about this Subject property can accidentally call them.



Conclusion: the only difference between RxRelay and Subject is the absence of two methods onComplete and onError, so that the developer cannot call the terminal state.



All Articles