이전 포스팅에서 MVVM을 사용하기 위해 LiveData와 코루틴을 사용한 프로젝트를 간략히 적고 내가 느낀 점을 적었다.
더 알아보다 보니 Rxjava를 사용해야지만 MVVM의 완성이라는 사람들도 존재하는 것 같아 공부하고 내 생각을 적어본다.
기본적으로 Reactive Extension 이라는 개념에서 나왔다고 한다.
많은 Event로 하여금 좀더 유연하게/유동적으로 변경된다는 말로 이해했다.
기본적으로 끊임없이 변하는 Data의 흐름을 알기 위해 사용한다.
두 가지의 역할? 이 존재한다
- Observable : Data의 Stream을 생성한다. Data를 발행한다.
- Subscriber : Data Stream에서 Data를 건져 사용한다. 사용을 안할 때는 사용해제를 해줘야 함.(사용 해제는 unsubscribe(), dispose() -> dispose는 완전히 사용하지 않게 될 때)
Observable - onNext() , onCompleted() , onError() 의 3가지 메소드가 존재하며
각각 onNext() - 새로운 Data를 구독하고 있는 Subscriber에게 전달
onCompleted() - Stream을 종료
onError() - 에러 발생시
계속해서 변하는 Data를 API를 통해 들어오는 Data라고 가정해보자.
어떠한 API를 통해 Data가 지속적으로 변화되어 들어올 때 onNext()를 통해 그 바뀐 Data를 뿌려 Subscriber가 변화된 Data를 알게 하는 것이다.
(이때, Data들을 filter, map 와 같은 operator를 통해 거르고 계산하여 반환하고 할 수 있다.)
RxAndroid - Rxjava를 사용하기 위해 Android의 Rxjava? 같은 느낌이다.
가장 큰 역할은 스케줄러는 제공하는 것이다.
뒤의 코드에서 더 설명하겠다.
예제로
온도를 받아오는 API를 사용하여 그 온도를 Rxjava와 RxAndroid를 사용해 화면에 뿌리는 App을 만들어보자.
API를 사용하는 것 대신 새로운 쓰레드를 생성하여 그 쓰레드가 온도를 뿌려주는 방법으로 진행하겠다.
MainActivity
class MainActivity : AppCompatActivity() {
val TAG = "RxAndroid.MainActivity"
val manager = TemperatureManager()
var disposable : Disposable?=null
var github = GithubClient()
lateinit var binding : ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil
.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
binding.currentTemperatureView.text = "받아온 온도가 없습니다."
val executorService = Executors.newSingleThreadScheduledExecutor()
executorService.scheduleAtFixedRate({
val nextTemperature = Random().nextInt(15)+10
manager.setTemperature(TemperatureManager.Temperature(nextTemperature))
}, 0L,3, TimeUnit.SECONDS)
disposable = manager.updateEvent().subscribe(this::updateView)
//subscribe하는 Item들을 updateView에서 처리하도록 하겠다 - java8식
}
override fun onDestroy() {
super.onDestroy()
disposable?.dispose()
}
fun updateView(temperature: TemperatureManager.Temperature)
{
binding.currentTemperatureView.text = "현재온도 : ${temperature.currentTemperature}"
}
}
TemperatureManager
class TemperatureManager
{
class Temperature(degree : Int)
{
var currentTemperature =degree
}
var subject : PublishSubject<Temperature> = PublishSubject.create()
//PublishSubject가 생성하는 subject는 Observable을 UI Event와 연결지어서 사용하고 싶을 때
//유저가 화면을 언제 터치할지 모를 때 -> 이럴 때 subject를 사용
//사용 하는 녀석
// 차이점 : Observable은 내부에서 Data를 emit하지만 subject는 코드처럼 외부에서 Data를 주입가능
// 아래의 함수 setTemperature()에서 onNext로 데이터를 전달하는 부분을 주목
fun setTemperature(temperature : Temperature)
{
subject.onNext(temperature)
}
//updateEvent에서 subscribeOn과 observeOn은 subscribe와 observe를 전담할 쓰레드를 지정
//UI Update는 메인쓰레드에서 실행해야하므로 AndroidSchedulers.mainThread()를 지정
fun updateEvent() : Observable<Temperature>
{
return subject.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
}
}
코드를 좀더 자세히 알아보자
binding = DataBindingUtil
.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
binding.currentTemperatureView.text = "받아온 온도가 없습니다."
binding을 사용하여 layout을 관리한다.
val executorService = Executors.newSingleThreadScheduledExecutor()
executorService.scheduleAtFixedRate({
val nextTemperature = Random().nextInt(15)+10
manager.setTemperature(TemperatureManager.Temperature(nextTemperature))
}, 0L,3, TimeUnit.SECONDS)
3초마다 새로운 온도를 생성하는 쓰레드를 생성한다.(API를 대신하는 역할)
disposable = manager.updateEvent().subscribe(this::updateView)
//subscribe하는 Item들을 updateView에서 처리하도록 하겠다 - java8식
TemperatureManager에는 주석으로 적어놨다.