Prologue

Android 앱을 사용하다보면 Back Button을 클릭했을 때, 바로 앱이 종료되는 경우와 토스트 메세지가 보여지는 경우가 있다. 이때, Back Button 처리를 위해 RxJava의 Subject를 활용하는 방법을 알아보기 위해 Subject에 대해 먼저 알아보려고 한다.

Subject

About Subject

Subject 클래스는 구독하고 있는 관찰자(Observer)에게 새로운 값을 전달할 때 사용하는 클래스이다. 따로 Observable로 새로운 값을 만들 필요없이 Subject 객체에 내장된 onNext 함수로 새로운 값을 옵저버에게 전달할 수 있기 때문에 짧은 코드로도 reactive하게 구현하는 게 가능하다. Android에서 제공하는 LiveData와 유사한 역할을 하기도 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import io.reactivex.subjects.PublishSubject
import util.Log

/**
* created by victory_woo on 2020/10/28
* */

fun main(args: Array<String>) {
val publishName: PublishSubject<String> = PublishSubject.create()

publishName.subscribe {
Log.d("Woo94", it)
}

publishName.onNext("Lee")
publishName.onNext("Park")
}

// 결과
Lee
Park

PublishSubject vs BehaviorSubject

RxJava에서는 AsyncSubject, PublishSubject, BehaviorSubject, RelaySubject가 있지만, 이번에는 가장 많이 사용되는 PublishSubject와 BehaviorSubject에 대해서 알아보겠다.

먼저, 코드를 통해 차이점을 파악한 뒤 특징에 대해 알아보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import io.reactivex.subjects.BehaviorSubject
import io.reactivex.subjects.PublishSubject
import util.Log

/**
* created by victory_woo on 2020/10/28
* */

fun main(args: Array<String>) {
val person = Person()
person.setNextName("Lee")
person.publishName.subscribe {
Log.d("Woo94", "publishName : $it")
}

person.behaviorName.subscribe {
Log.d("Woo94", "behaviorName : $it")
}

person.setNextName("Kim")
}

class Person {
var behaviorName: BehaviorSubject<String> = BehaviorSubject.create()
var publishName: PublishSubject<String> = PublishSubject.create()

fun setNextName(name: String) {
behaviorName.onNext(name)
publishName.onNext(name)
}
}

Person 클래스에 2개의 Subject 객체를 선언하였고 setNextName() 함수를 호출하여, subject 객체의 값을 한번에 바꾸려고 한다. 그리고 publishName과 behaviorName을 구독하기 전에 이름을 "Lee"로 갱신을 미리 했다.

그리고 코드의 출력 결과는 다음과 같다.

1
2
3
main | Woo94 | debug = behaviorName : Lee
main | Woo94 | debug = behaviorName : Kim
main | Woo94 | debug = publishName : Kim

behaiorName은 구독 전에 갱신한 “Lee” 문자열을 출력하는 반면, publishName은 구독 이후에 갱신한 “Kim” 문자열만 출력하고 있다. 이는 두 객체의 동작 구조가 다르기 때문인데, 이제부터 차이점에 대해 살펴보도록 하자.

PublishSubject

  • 구독 이후에 발행한 값에 대해서만 값을 받는다.
  • 아래 다이어그램의 세번째 줄에서 구독하기 이전에 갱신된 빨간공, 초록공은 무시하고 파란공만 받고 있는 것을 볼 수 있다.
  • 과거의 데이터를 무시하고 새로 갱신되는 값만 보고 싶은 경우 사용하면 좋다.
  • 대표적으로 버튼을 클릭하는 이벤트를 PublishSubject로 사용하기도 한다.

BehaviorSubject

  • 구독하는 시점의 가장 최근에 갱신된 값을 받는다.
  • 아래 다이어그램의 세번째 줄에서 구독하면 가장 최근에 갱신된 초록색 공과 그 이후에 갱신된 파란색 공을 받는 것을 볼 수 있다.
  • 구독하는 시점에서 과거에 갱신된 데이터 중 가장 최근의 값이 필요할 때 사용하면 유용하다.
  • 또한, 초기값을 지정할 수도 있으며, 구독하게 되면 초기 데이터를 먼저 받을 수 있다.
  • Android에서 Back Button 처리를 위해 BehaviorSubject를 활용하기도 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fun test(){
val subject: BehaviorSubject<String> = BehaviorSubject.createDefault("6")
subject.subscribe { data ->
println("Subscriber #1 => $data")
}

subject.onNext("1")
subject.onNext("3")
subject.subscribe { data ->
println("Subscriber #2 => $data")
}

subject.onNext("5")
subject.onComplete()
}
// 결과
Subscriber #1 => 6
Subscriber #1 => 1
Subscriber #1 => 3
Subscriber #2 => 3
Subscriber #1 => 5
Subscriber #2 => 5

Ref