Android/RxJava, RxKotlin

RxJava - subscribeOn, observeOn 를 제대로 쓰고 있는걸까?

최데브 2021. 10. 20. 14:59
반응형

해당 글은 https://vagabond95.me/posts/is-this-rxjava-1/

 

[안드로이드] 그런 Rx Java 로 괜찮은가 1 - subscribeOn, observeOn - 기록은 기억을 지배한다

구글은 점점 더 안드로이드 아키텍처를 리엑티브하게 구조화 하려는 움직임을 보이고 있고, Databinding, LiveData 그리고 RxJava 는 그러한 구조화 작업의 핵심 토대를 담당하고 있다. 또한 주력으로

vagabond95.me

을 읽고 깊게 Rx에 대해 고민하지 않았던 스스로를 반성하는 포스팅임을 밝힙니다. ㅜㅜ

 

rx 를 사용하다보면 subscribeOn, observeOn 를 자연스럽게 만나게 된다. 

나도 개인 프로젝트에 rx와 retrofit 을 연동해서 값을 받아오고 그 값을 읽을때 항상 쓰고 있는데 쓰면서도 

그냥 왜 있는지 정도만 알고 자세하게 고민해본적은 없었다. 그런 의미에서 위 링크에 있는 좋은 예가 있어서 들어보겠다.

 

Schedular 퀴즈

// # 1
myService.getUsers()
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .flatMap(Observable::fromIterable)
    .filter(User::isMember)
    .map(this::saveToCache)
    .toList()
    .subscribe(View::showUser);
    
    
// # 2
myService.getUsers()
    .subscribeOn(Schedulers.io())
    .flatMap(Observable::fromIterable)
    .filter(User::isMember)
    .map(this::saveToCache)
    .toList()
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(View::showUser);

 

둘다 api 를 호출해서 수행하는 로직이다.

두개의 차이점이라면 1번의 경우 subscribeOn, observeOn 의 호출 순서가 다르다.

 

나의 경우는 1번처럼만 고정적으로 사용했는데 과연 어떤 차이점이 있을까?

차이점은

1번의 경우 getUser()에 대한 연산만 io Schedular 쓰레드 위에서 수해오디고 나머지 모든 하위 스트림연산은 메인 스레드에서 수행된다.

2번의 경우는 getUsers() ~ toList 연산까지 io Schedular 위에서 수행되며, subscribe 내에서 콜백으로 최종 데이터를 전달받는 연산만 메인쓰레드에서 수행된다.

 

느낌이 온다. 메인스레드에서는 UI 만 변경하는 작업만 하는게 좋다. 안드로이드 라이프사이클상 메인스레드에서 많은 연산을 처리하면 그 연산을 하는동안 앱이 다른 동작을 못하는 먹통이 되기도 한다. 

간단한 연산이라면 큰 문제가 없겠지만 큰 연산이 필요하고 오래 걸리는 작업이라면 엄청난 자원낭비가 일어난다. 

 

다시 한번 정리해보자. 

먼저 subscribeOn 은

  • observable source 가 observer 에 의해 subscribe 됐을 때, source 가 데이터를 다음 스트림으로 전달하는 액션을 수행하는 스케쥴러를 지정.

observeOn 은

  • observerOn 이후 수행되는 스트림의 액션을 수행하는 스케쥴러를 지정

이다. 

 

어떤 순서로 호출하냐에 따라서 어떤 동적을 어떤 스레드에서 처리할것인지 흐름을 정할 수 있는것이다. 적절하게 사용하면 동작에 맞는 스레드를 분배하여 더욱 좋은 프로그램을 만들 수 있게 되는 것이다.

 

위의 코드는 예시로 든것이고 실제로 저 동작을 처리하기에 적당한 코드는 

myService.getUsers()
    .subscribeOn(Schedulers.io())
    .observeOn(Schedulers.computation()) 
    .flatMap(Observable::fromIterable)
    .filter(User::isMember)
    .map(this::saveToCache)
    .toList()
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(View::showUser);

이렇다. Schedulers.computation() 는 연산에 적합한 스레드로 처리하게 해준다. 

io 로 데이터를 가져오고 computation 으로 연산하도록 스트림을 넘겨준뒤 

 

.flatMap(Observable::fromIterable)
.filter(User::isMember)
.map(this::saveToCache)
.toList()

 

의 연산을 실행하고 나온 결과를 mainThread로 처리하여 UI에 반영하면 각 동작에 적합한 스레드를 사용하며 하나의 테스트를 완료하게 된다.

 

멋지다. 아주!

 

 

추가적으로

subscribeOn  은 최초 호출에만 적용되며 그 뒤의 호출은 무시된다고한다.

observeOn 은 횟수제한없이 호출이 가능하다.

subscribeOn 와 observeOn  는 옵션일뿐 꼭 쓸 필요는 없다. -> 둘다 적지 않았다면 subscribe 를 호출한 thread 에서 스트림연산이 수행된다. 일반적으로는 메인쓰레드다. 

 

 

 

 

 

 

 

반응형