Seoplee
개발의 섭리, Seoplee의 개발
Seoplee
  • 분류 전체보기 (54)
    • Android (26)
      • Architecture (12)
      • Compose (0)
      • Tips (11)
      • 트러블슈팅 (3)
    • IOS (1)
      • Tips (1)
    • Kotlin (1)
    • Coroutine (3)
      • Flow (3)
    • RxJava (12)
    • CI&CD (1)
    • WEB (8)
    • Network (1)
    • ETC (1)
    • (임시) (0)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
Seoplee

개발의 섭리, Seoplee의 개발

RxJava

RxJava 안드로이드 프로젝트에 적용하기 01 (with Sunflower)

2022. 4. 5. 01:34

RxJava 안드로이드 프로젝트에 적용하기 (with Sunflower)

RxJava에 관한 책을 정리한 후 안드로이드 프로젝트에 적용하여 학습하는 방법으로, Google의 Android Sunflower 프로젝트 내부의 코드를 Rx로 migration하는것을 목표로 한다.

Coroutine Flow -> RxJava

Rx는 Single, Maybe, Completable 등 활용할 수 있는 다양한 클래스가 존재한다.

Single

  • 데이터를 1건만 통지하거나 에러를 통지
  • 데이터 통지가 곧 완료 통지이므로 별도의 완료 통지 안함
  • onNext와 onComplete 대신 onSuccess 제공

Maybe

  • 데이터를 1건만 통지하거나, 1건도 통지않거나 에러 통지
  • 데이터가 1건도 없이 정상종료 될 때만 onComplete 호출
  • onNext 대신 onSuccess 제공

Completable

  • 데이터를 통지하지 않거나 에러 통지
  • Completable 내에서 특정 부가 작용이 발생하는 처리 수행
  • 해당 처리가 끝나면 onComplete, 에러시 onError 호출

다음은 Flow를 사용하는 sunflower의 PlantDao의 일부분이다.

@Query("SELECT * FROM plants ORDER BY name")
fun getPlants(): Flow<List<Plant>>

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAll(plants: List<Plant>)

Room은 Flow뿐 아니라 Rx도 지원하므로, 위 코드는 다음과 같이 Rx를 사용한 코드로 변경할 수 있다.

@Query("SELECT * FROM Plant")
fun getPlants(): Flowable<List<Plant>>

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(plants: List<Plant>) : Completable

Rx의 연산자중 Completable은 데이터 자체를 반환하지는 않고 onComplete, onError만 알리기 때문에, 특별한 Response가 필요 없는 경우 사용하기에 적합하다.

db.plantDao().insertAll(plantList)
    .subscribeOn(Schedulers.io())
    .subscribe(
        { Log.d(TAG,"doWorkSuccess") },
        { Log.e(TAG, "Error seeding database - insert Error : ${it.message}") }
    )

스트림을 통해 받아온 데이터로 ui 작업을 해야 하는 경우,
다음과 같이 ui를 관리하는 스케줄러인 AndroidSchedulers.mainThread() 를 할당하여 사용한다.

plantRepository.getPlants()
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(
        { data -> _plantList.postValue(data) },
        { Log.e(TAG, "loadPlantList Error : ${it.message}") }
    )

한번의 Request & Response로 이루어져 일반적으로 한번의 값만을 방출하는 경우 (api 통신, client-server request/response 등) 다음과 같이 Flowable 대신 Single 혹은 Maybe 연산자를 사용할 수도 있다.

@Query("SELECT * FROM Plant WHERE plant_id = :plantId")
fun getPlant(plantId: String): Single<Plant>

Rx 스트림에 안드로이드 생명주기 연결하기

Flow는 asLiveData 혹은 repeatOnLifecycle 등을 이용하여 각종 컴포넌트의 생명주기에 쉽게 연결할 수 있지만, Rx는 직접 생명주기를 설정해주어야 한다.

Rx에서는 스트림 구독이 필요 없을 때, dispose할 수 있으며, 이를 위해 Disposable 이라는 인터페이스를 제공한다.
해당 객체를 이용하면 onComplete가 호출되지 않아도 dispose하여 강제로 중단시킬 수 있다.

fun main() {
    val observable: Observable<Long> = Observable.interval(1, TimeUnit.SECONDS)
    val disposable: Disposable = observable.subscribe { println(it) }

    Thread.sleep(3000)
    disposable.dispose()
}

실행 결과

0
1
2
Process finished with exit code 0

또한 observable이 여러개 존재할 경우 이를 한번에 관리하기 위하여 CompositeDisposable 를 사용할 수 있다.
여러 observable을 add로 추가하여 사용한다.

compositeDisposable.add(
    gardenPlantingRepository.createGardenPlanting(plantId)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(
            { Log.d(TAG,"addPlantToGarden Success") },
            { Log.e(TAG,"addPlantToGarden Fail : ${it.message}")}
        )
)

안드로이드에서는 상황에따라 onDestroy 혹은 onPause에서 주로 dispose하여 생명주기와 연결시켜 관리할 수 있다.
Rx를 사용하는 모든 뷰에 연결할 것이므로 보일러플레이트를 줄이기위해 BaseView와 BaseViewModel을 만들어 작업하였다.

abstract class BaseFragment<VM : BaseViewModel> : Fragment() {

    abstract val viewModel : VM

    override fun onDestroy() {
        super.onDestroy()
        viewModel.clearCompositeDisposable()
    }
}
abstract class BaseViewModel : ViewModel() {
    protected val compositeDisposable = CompositeDisposable()

    fun clearCompositeDisposable() = compositeDisposable.clear()

    override fun onCleared() {
        super.onCleared()
        compositeDisposable.dispose()
    }
}
저작자표시 (새창열림)

'RxJava' 카테고리의 다른 글

RxJava 안드로이드 프로젝트에 적용하기 02 (with Paging 3)  (0) 2022.04.05
RxJava 06 - RxJava의 디버깅과 테스트  (0) 2022.02.16
RxJava 05 - Processor/Subject  (0) 2022.02.16
RxJava 04 - Flowable과 Observable의 연산자 (part.04)  (0) 2022.01.25
RxJava 04 - Flowable과 Observable의 연산자 (part.03)  (0) 2022.01.18
    'RxJava' 카테고리의 다른 글
    • RxJava 안드로이드 프로젝트에 적용하기 02 (with Paging 3)
    • RxJava 06 - RxJava의 디버깅과 테스트
    • RxJava 05 - Processor/Subject
    • RxJava 04 - Flowable과 Observable의 연산자 (part.04)
    Seoplee
    Seoplee
    개발공부를 하며 기록할만한 것들을 정리해놓은 블로그입니다.

    티스토리툴바