이번편에서도 코드를 보면서 학습하길 권한다. (나도 학습하면서 포스팅 중)
Model
Model은 View에 표시할 데이터를 의미한다. DataModel이라고도 하며 DB, Network, SharedPreference 등 다양한 데이터 소스로부터 필요한 데이터를 준비한다. ViewModel에서 데이터를 가져갈 수 있게 데이터를 준비하고 그에 대한 "이벤트"를 보낸다.
DataModel, DataModelImpl
DataModel를 interface로 만들어서 구현부(DataModelImpl)를 분리시켰다. ViewModel이 데이터를 가지고 갈 수 있도록 준비되어 있다. 데이터를 가지고 올 소스를 API로 받아야하기에 KakaoSearchService를 구현했다. API의 파라미터 중 정해진 값만 전달되도록 KakaoSearchSortEnum도 구현했다. 서버에서 받은 데이터를 담는 역할로 ImageSearchResponse를 생성했다. 그런데 이 ImageSearchResponse를 Single의 제너릭 안에 두고 있다. 이 Single이 무엇일까?
Single
Single을 이해하기 위해서는 리액티브 프로그래밍을 이해해야 한다. 그런데 여기서 설명하기에 Rx는 너무 방대하다. 그래서 간단하게만 설명을 해보자면...
- 리액티브 프로그래밍(Rx)는요...
- 함수형 프로그래밍 도구를 활용한 비동기 프로그래밍
- RxJava는 클라이언트의 요청을 처리할 때 다수의 비동기 실행 흐름(스레드 등)을 생성하고 그것의 결과를 취합하여 최종 리턴하는 방식
- 비동기 흐름을 조합할 수 있는 방법을 제공한다. RxJava에서는 조합하는 실행 단위를 리액티브 연산자라고 한다.
- 비동기 방식으로 동작하는 가장 대표적인 프로그래밍 패턴은 콜백이다. 그래서 RxJava는 콜백을 사용하지 않는 방향으로 설계하여 이를 해결한다.
그렇다면 Single은 무엇일까? (www.taeiim.tistory.com/ 참고)
- Single 전에... 우선 Observable 클래스
- 옵서버(observer) 패턴을 구현
- 옵서버 패턴은 객체의 상태 변화를 관찰하는 관찰자(옵서버) 목록을 객체에 등록
- 상태 변화가 있을 때마다 메서드를 호출하여 객체가 직접 목록의 각 옵서버에게 변화를 알려준다.
라이브사이클은 존재하지 않으며, 보통 단일 함수를 통해 변화만 알린다. - 관찰자(Observer)가 관찰하는 대상
- Observed라는 단어가 관찰을 통해서 얻은 결과를 의미한다면, Observable은 현재는 관찰되지 않았지만 이론을 통해서 앞으로 관찰할 가능성을 의미한다.
- Single 클래스
- Observable의 특수한 형태 (1.x 버전부터 존재)
- Observable 클래스는 데이터를 무한하게 발행할 수 있지만, Single 클래스는 오직 1개의 데이터만 발행하도록 한정
- 보통 결과과 유일한 서버 API를 호출할 때 유용하게 사용
- 데이터 하나가 발행과 동시에 종료
- 라이프사이클 관점에서 onNext() / onComplete() 함수가 onSuccess() 로 통합
- onSuccess(T value) / onError() 함수로 구성
이제 개념은 그렇다고 하니 하나씩 뜯어보자.
interface DataModel {
fun getData(query:String, sort: KakaoSearchSortEnum, page:Int, size:Int): Single<ImageSearchResponse>
}
return 타입이 Single이고 제너릭(제네릭인가?)에 ImageSearchResponse가 들어가있다. 이는 onSuccess(T value) 함수에서 ImageSearchResponse를 돌려준다는 것으로 이해하면 된다. 그러면 이 메소드를 사용하는 곳은 어딜까?
fun getImageSearch(query: String, page: Int, size: Int) {
addDisposable(model.getData(query, KakaoSearchSortEnum.Accuracy, page, size)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
it.run {
if (documents.size > 0) {
Log.d(TAG, "documents : $documents")
_imageSearchResponseLiveData.postValue(this)
}
Log.d(TAG, "meta : $meta")
}
}, {
Log.d(TAG, "response error, message : ${it.message}")
})
)
}
addDisposable 메소드 내에 있는 getData로 데이터를 발행한다. 그 밑에 각종 메소드를 살펴보자.
- Schedulers
- Observable 연산자 체인에 멀티스레딩을 적용하고 싶다면, 특정 스케줄러를 사용해서 연산자(또는 특정 Observable)를 실행하면 된다. (Rx 홈페이지에 나와있는 설명)
- Schedulers는 옵저버블 연결에서 실행되는 작업들을 언제-어디서 실행할지 결정할 수 있게 해 준다.
- SubscribeOn
- SubscribeOn은 구독(subscribe)에서 사용할 스레드를 지정
- 도중 ObserveOn이 호출되어도 SubscribeOn의 스레드 지정에는 영향을 끼치지 않는다.
- IO 스케줄러(Schedulers.io())
- 계산 스케줄러와는 다르게 네트워크상의 요청을 처리하거나 각종 입,출력 작업을 실행하기 위한 스케줄러
- ObserveOn
- ObserveOn은 Observable이 다음 처리를 진행할때 사용할 스레드를 지정
- ObserveOn이 선언된 후 처리가 진행 뒤 다른 ObserveOn이 선언시 다른 ObserveOn에서 선언한 스레드로 변경되어 이후 처리를 진행한다.
- ()
- RxJava는 내가 동작시키기 원하는 것을 사전에 정의해 둔 다음 실제 그것이 실행되는 시점을 조절할 수 있다. 이 때 사용하는것이 subscribe()함수이다.
- Observable은 just() 등의 팩토리 함수로 데이터 흐름을 정의한 후, subscribe() 함수를 호출해야 실제로 데이터를 발행합니다.
- onSucces(T value) : it.run{...} 부분
- onError() : response error가 Log로 출력되는 부분
ObserveOn
Rx에 대해서는 다음에 더 자세히 다루기로하고 이번에는 이렇게 수박겉핥기만하고 넘어가자. MVVM에 대해서 알아야 되는게 너무 너무 너무 너무 많다. (주르륵)
class MainViewModel(private val model: DataModel) : BaseViewModel() {...}
MainViewModel은 BaseViewModel을 상속 받는데 여기서 addDisposable을 이용한다. RxJava를 사용할 때 스트림을 취소해주지 않으면 계속 돌기 때문에 위험하다. (메모리 릭 발생) 이를 해결하기 위해서는 CompositeDisposable이라는 어레이 스트림에 만들어진 코드를 넣어두고 한 번에 취소하는 방법을 사용한다. CompositeDisposable 변수를 선언하고 생명 주기 변경이 일어나면 clear로 지워준다.
참고
- http://reactivex.io/documentation/ko/single.html
- https://taeiim.tistory.com/entry/RxJava2-2-Observable-Single-Maybe-%EB%9C%A8%EA%B1%B0%EC%9A%B4%EC%B0%A8%EA%B0%80%EC%9A%B4-Observable-%ED%8C%A9%ED%86%A0%EB%A6%AC%ED%95%A8%EC%88%98?category=759183
-
https://taeiim.tistory.com/entry/RxJava2-5-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%9F%AC
-
https://dnight.tistory.com/entry/SubscribeOn-%EC%99%80-ObserveOn-%EA%B7%B8%EB%A6%AC%EA%B3%A0-Schedulers
'Android, iOS' 카테고리의 다른 글
[MVVM 정복] 5. View에 Koin으로 의존성 주입하기 (0) | 2020.05.18 |
---|---|
[MVVM 정복] 4. 어렵고 이해도 잘 안되는 DI, 그리고 Koin (0) | 2020.05.18 |
[MVVM 정복] 2. SnackbarMessage, BaseViewModel, BaseActivity (0) | 2020.05.18 |
[MVVM 정복] 1. LiveData와 SingleLiveEvent (0) | 2020.05.18 |
[MVVM 정복] 0. 어떤 순서로 개발할 것인가 (0) | 2020.05.18 |
Comment