[1B4]안드로이드 동시성_프로그래밍
-
Upload
naver-d2 -
Category
Technology
-
view
1.823 -
download
7
description
Transcript of [1B4]안드로이드 동시성_프로그래밍
안드로이드 동시성 프로그래밍 - RxJava를 활용한 Functional Reactive Programming
최정열 / Sivaworks / 라 스칼라 코딩단email: [email protected]
조현태 / Line + / 라 스칼라 코딩단email: [email protected]
오늘의 목표
오늘의 목표
함수형 프로그래밍에 한 번 관심을 가져보시는 것은 어떨까요?!함수형을 프로그래밍을 익히는 것이 가장 큰 도전…!더 어려운 부분은 절차형과 함수형에서 효율적인 방법을 선택하는 것!!라 스칼라 코딩단: https://groups.google.com/forum/#!forum/scala-korea!샘플 저장소: https://github.com/orgs/FridayCoders/dashboard (업데이트 예정)!
1. Plain Old Java Concurrent Programming2. 생명주기와 동시성 프로그래밍3. Sequencial Tasks & Parellel Tasks4. 람다 & 함수형 프로그래밍
안드로이드 동시성 프로그래밍
동시성 관련클래스 & 패키지
Android Concurrent Programming
Concurrent Programming
Thread
concurrent package
Handler
AsyncTask
Service
AsyncQueryHandler
Loader
Volley
OkHttp, Retrofit, Picasso
AndroidUniversalImageLoader
AndroidAsync(Http), ION
AndroidAnnotations
Guava
greenrobot/EventBus
otto
Thread
Concurrent Programming
new Thread() { @Override public void run() { URL url = new URL(strUrl); HttpURLConnection urlConnection = ... urlConnection.connect(); iStream = urlConnection.getInputStream(); bitmap = BitmapFactory.decodeStream(iStream); } }.start();
Background
메인 쓰레드의 메시지큐로 전달
Thread class
new Thread() { @Override public void run() { URL url = new URL(strUrl); HttpURLConnection urlConnection = ... urlConnection.connect(); iStream = urlConnection.getInputStream(); bitmap = BitmapFactory.decodeStream(iStream); ! runOnUiThread(new Runnable() { @Override public void run() { imageView.setImageBitmap(bitmap); } } }.start();
UI Thread
예외 발생!
Thread class
new Thread() { @Override public void run() { try{ URL url = new URL(strUrl); HttpURLConnection urlConnection = ... urlConnection.connect(); iStream = urlConnection.getInputStream(); bitmap = BitmapFactory.decodeStream(iStream); } catch (Exception e){ log(e.toString); } runOnUiThread(new Runnable() { @Override public void run() { imageView.setImageBitmap(bitmap); } } }.start();
Background
예외는 백그라운드에서 발생하지만, 메시지는 UI쓰레드로…
Thread class
new Thread() { @Override public void run() { try{ URL url = new URL(strUrl); HttpURLConnection urlConnection = ... urlConnection.connect(); iStream = urlConnection.getInputStream(); bitmap = BitmapFactory.decodeStream(iStream); } catch (Exception e){ log(e.toString); runOnUiThread(new Runnable(){ showToast(“다운로드가 실패했습니다.”); }); } runOnUiThread(new Runnable() { @Override public void run() { imageView.setImageBitmap(bitmap); } } }.start();
Background
UI Thread
예외 처리 …
Thread class
... try{ URL url = new URL(strUrl); HttpURLConnection urlConnection = ... urlConnection.connect(); iStream = urlConnection.getInputStream(); bitmap = BitmapFactory.decodeStream(iStream); } catch (MalformedURLException e){ log.e(e.toString); } catch (IOException e){ runOnUiThread(new Runnable(){ showToast(“다운로드가 실패했습니다.”); }); } catch (Exception e){ // ... } ...
리팩토링
Thread class
Bitmap downloadUrl(String strUrl) { try { iStream = getInputStream(strUrl); } catch (Exception e) { handleException(e, new OnError(){ String onError(){ return “다운로드 실패”; } }); } finally { close(iStream); } return BitmapFactory.decodeStream(iStream); } !private void handleException(Exception e, OnError onError) { ... }
보기 편하게 람다식으로…
구현을 보지 않으면 어떤 쓰레들에서 동작하는지 모르겠다…
Thread class
Bitmap downloadUrl(String strUrl) { try { iStream = getInputStream(strUrl); } catch (Exception e) { handleException(e, ()-> { “다운로드 실패” }); } finally { close(iStream); } return BitmapFactory.decodeStream(iStream); } !private void handleException(Exception e, OnError onError) { ... }
이 메소드는 백그라운드? 아님, UI Trehad? !
구현을 들여다 보지 않으면 모르겠는걸?
현재 쓰레드에서 메소드 호출과 UI쓰레드 호출을 비교
Thread class
Bitmap downloadUrl(String strUrl) { ... handleException(e, ()-> “다운로드 실패” ); } !!!// 1 void handleException(Exception e, OnError onError) { runOnUiThread(new Runnable(){ void run(){ log.d(onError.call());} } } !// 2 void handleException(Exception e, OnError onError) { log.d(onError.call()); } 현재 쓰레드 (Background)
UI 쓰레드
로깅은 백그라운드에서 처리해도 되는데, UI 쓰레드에서 호출하는건 낭비 닐까?
오류를 뷰에 반영하고 싶은데…
Thread class
Bitmap downloadUrl(String strUrl) { ... handleException(e, ()-> { Toast.makeText(getActivity(), ()-> { textView.setText(“다운로드 실패”) }}); } !// 1 void handleException(Exception e, OnError onError) { runOnUiThread(new Runnable(){ void run(){ onError.call(); } } } !// 2 void handleException(Exception e, OnError onError) { onError.call(); }
뷰를 조작하는 코드를 전달 하면?
CalledFromWrongThreadException
Thread class
Bitmap downloadUrl(String strUrl) { ... handleException(e, ()-> { Toast.makeText(getActivity(), ()-> { textView.setText(“다운로드 실패”) }}); } !// 1 void handleException(Exception e, OnError onError) { runOnUiThread(new Runnable(){ void run(){ onError.call(); } } } !// 2 -> CalledFromWrongThreadException void handleException(Exception e, OnError onError) { onError.call(); }
UI 쓰레드 외에서는 뷰를 조작 할 수 없음!
모든 메소드에 어떤 쓰레드에서 작동하는지 이름을 붙여보자.
Thread class
Bitmap downloadUrl(String strUrl) { ... handleException(e, ()-> { Toast.makeText(getActivity(), ()-> { textView.setText(“다운로드 실패”) }}); } !// 1 void handleExceptionOnBgThread(Exception e, OnError onError) { runOnUiThread(new Runnable(){ void run(){ onError.call(); } } } !// 2 void handleExceptionOnUiThread(Exception e, OnError onError) { onError.call(); }
모든 메소드의 이름을 이렇게 짓는 것은 무리야!
생명주기와 쓰레드
Life Cycle
쓰레드를 시작했으면 독립적으로 존재
생명주기와 쓰레드
ThreadBackground
UI Thread Activity A
onDestroy()
Activity B
Activity A가 종료 되어도 쓰레드는 독립적으로 살아 있음
Inner class 사용시 메무리 누수
생명주기와 쓰레드
ThreadBackground
UI ThreadActivity A
Reference
Activity B
onDestroy()
Inner class로 쓰레드를 만들면 암북적으로 액티비티를 참조
쓰레드가 암묵적으로 참조하고 있어서, onDestroy()가 호출 된
후에도, GC되지 않음!
성능 저하, 메모리 낭비
쓰레드를 종료 시킬 깔끔한 방법의 필요성
생명주기와 쓰레드
ThreadBackground
UI Thread Activity A
어디선간 쓰레드를 종료 해야겠네!
onStop() onDestroy()
어떻게 종료 시키지? interrupt?
또, try ~ catch ~ final?
Callback 을 사용한 Thread class
생명주기와 쓰레드
MyThread extends Thread{ interface MyCallback { void onFinishDownloadImage(T t); } ! MyCallback callback; ! @Override public void run() { // download image... runOnUiThread(new Runnable() { @Override public void run() { callback.onFinishDownloadImage(bitmap); } } };
모든 blocking 작업이 끝나면 콜백을 통해 결과를 전달~
예외 발생
생명주기와 쓰레드
MyThread extends Thread{ interface MyCallback { void onFinishDownloadImage(T t); void onError(Exception e); } ! MyCallback callback; ! @Override public void run() { try { // download image… } catch { callback.onError(e); } runOnUiThread(new Runnable() { @Override public void run() { callback.onFinishDownloadImage(bitmap); } } };
예외 발생시 유저에게 알려야 하는데, UI요소에는 직접 처리 할 수 없으므로
콜백 메소드를 추가.
Android Concurrent Programming
Concurrent Programming
Thread
concurrent package
Handler
AsyncTask
Service
AsyncQueryHandler
Loader
Volley
OkHttp, Retrofit, Picasso
AndroidUniversalImageLoader
AndroidAsync(Http), ION
AndroidAnnotations
Guava
greenrobot/EventBus
otto
Executor + Thread pool + Future …
Thread 보다 추상화된 제어가 가능!
Android Concurrent Programming
Concurrent Programming
Thread
concurrent package
Handler
AsyncTask
Service
AsyncQueryHandler
Loader
Volley
OkHttp, Retrofit, Picasso
AndroidUniversalImageLoader
AndroidAsync(Http), ION
AndroidAnnotations
Guava
greenrobot/EventBus
otto
안드로이드의 UI Thread를 관리하는 놈.
Looper와 함께 사용하고, Looper는 메시지
큐를 가지고 있어서 순차 실행을 보장!
Android Concurrent Programming
Concurrent Programming
Thread
concurrent package
Handler
AsyncTask
Service
AsyncQueryHandler
Loader
Volley
OkHttp, Retrofit, Picasso
AndroidUniversalImageLoader
AndroidAsync(Http), ION
AndroidAnnotations
Guava
greenrobot/EventBus
otto
Handler + 탬플릿 메소드 패턴
다운로드 + Ui + 예외 처리 (?)
AsyncTask class
@Override protected void onPreExecute() { progressBar.setVisibility(View.VISIBLE); } !@Override protected Bitmap doInBackground(String... params) { try { bitmap = downloadBitmap(params); } catch (Exception e) {...} return bitmap; } !@Override protected void onPostExecute(Bitmap bitmap) { imageView.setImageBitmap(bitmap); progressBar.setVisibility(View.GONE); }
UI 쓰레드
다운로드 + Ui + 예외 처리 (?)
AsyncTask class
@Override protected void onPreExecute() { progressBar.setVisibility(View.VISIBLE); } !@Override protected Bitmap doInBackground(String... params) { try { bitmap = downloadBitmap(params); } catch (Exception e) {...} return bitmap; } !@Override protected void onPostExecute(Bitmap bitmap) { imageView.setImageBitmap(bitmap); progressBar.setVisibility(View.GONE); }
백그라운드 쓰레드
다운로드 + Ui + 예외 처리 (?)
AsyncTask class
@Override protected void onPreExecute() { progressBar.setVisibility(View.VISIBLE); } !@Override protected Bitmap doInBackground(String... params) { try { bitmap = downloadBitmap(params); } catch (Exception e) {...} return bitmap; } !@Override protected void onPostExecute(Bitmap bitmap) { imageView.setImageBitmap(bitmap); progressBar.setVisibility(View.GONE); }
예외는 백그라운드 쓰레드에서
발생하는데, UI Thread로 어떻
게 전달하지?
Android Concurrent Programming
Concurrent Programming
Thread
concurrent package
Handler
AsyncTask
Service
AsyncQueryHandler
Loader
Volley
OkHttp, Retrofit, Picasso
AndroidUniversalImageLoader
AndroidAsync(Http), ION
AndroidAnnotations
Guava
greenrobot/EventBus
otto
액티비티의 생명 주기로 부터
영향을 받지 않음!
Android Concurrent Programming
Concurrent Programming
Thread
concurrent package
Handler
AsyncTask
Service
AsyncQueryHandler
Loader
Volley
OkHttp, Retrofit, Picasso
AndroidUniversalImageLoader
AndroidAsync(Http), ION
AndroidAnnotations
Guava
greenrobot/EventBus
otto
커서관리 방법 없고,
AsyncTask와 문제점 공유
Android Concurrent Programming
Concurrent Programming
Thread
concurrent package
Handler
AsyncTask
Service
AsyncQueryHandler
Loader
Volley
OkHttp, Retrofit, Picasso
AndroidUniversalImageLoader
AndroidAsync(Http), ION
AndroidAnnotations
Guava
greenrobot/EventBus
otto
허니컴 이후 등장. compat을 이용해서 이전에도
사용이 가능하며 생명주기 변경시 스스로 처리함
단, ContentProvider 외에는 사용이 어려움
동시성 관련라이브러리
Third Party Libraries
Concurrent Programming
Thread
concurrent package
Handler
AsyncTask
Service
AsyncQueryHandler
Loader
Volley
OkHttp, Retrofit, Picasso
AndroidUniversalImageLoader
AndroidAsync(Http), ION
AndroidAnnotations
Guava
greenrobot/EventBus
otto
네트워크, 이미지
Callback 기반
Third Party Libraries
Concurrent Programming
Thread
concurrent package
Handler
AsyncTask
Service
AsyncQueryHandler
Loader
Volley
OkHttp, Retrofit, Picasso
AndroidUniversalImageLoader
AndroidAsync(Http), ION
AndroidAnnotations
Guava
greenrobot/EventBus
otto
동기, 비동기 (async) 지원
RxJava의 Observable 지원
Third Party Libraries
Concurrent Programming
Thread
concurrent package
Handler
AsyncTask
Service
AsyncQueryHandler
Loader
Volley
OkHttp, Retrofit, Picasso
AndroidUniversalImageLoader
AndroidAsync(Http), ION
AndroidAnnotations
Guava
greenrobot/EventBus
otto
Third Party Libraries
Concurrent Programming
Thread
concurrent package
Handler
AsyncTask
Service
AsyncQueryHandler
Loader
Volley
OkHttp, Retrofit, Picasso
AndroidUniversalImageLoader
AndroidAsync(Http), ION
AndroidAnnotations
Guava
greenrobot/EventBus
otto
Future
Third Party Libraries
Concurrent Programming
Thread
concurrent package
Handler
AsyncTask
Service
AsyncQueryHandler
Loader
Volley
OkHttp, Retrofit, Picasso
AndroidUniversalImageLoader
AndroidAsync(Http), ION
AndroidAnnotations
Guava
greenrobot/EventBus
otto
@Background 어노테이션
내부적으로 ScheduledThreadPool을 활용
Third Party Libraries
Concurrent Programming
Thread
concurrent package
Handler
AsyncTask
Service
AsyncQueryHandler
Loader
Volley
OkHttp, Retrofit, Picasso
AndroidUniversalImageLoader
AndroidAsync(Http), ION
AndroidAnnotations
Guava
greenrobot/EventBus
ottoListenableFuture
Third Party Libraries
Concurrent Programming
Thread
concurrent package
Handler
AsyncTask
Service
AsyncQueryHandler
Loader
Volley
OkHttp, Retrofit, Picasso
AndroidUniversalImageLoader
AndroidAsync(Http), ION
AndroidAnnotations
Guava
greenrobot/EventBus
otto
이벤트 버스
RxJava로 대체 할 수 있음
Third Party Libraries
Concurrent Programming
Thread
concurrent package
Handler
AsyncTask
Service
AsyncQueryHandler
Loader
Volley
OkHttp, Retrofit, Picasso
AndroidUniversalImageLoader
AndroidAsync(Http), ION
AndroidAnnotations
Guava
greenrobot/EventBus
otto
애플리케이션의 설계가 라이브러리 인터페이스에 의존적이 되는 것을 주의!
저마다 Http client, 이미지 캐싱, ThreadPool 관리 서로 다른 정책과 인터페이
스를 가지고 있으므로, 중복 설정 및 불필요한 추상화를 주의!
Android Event Sources
Concurrent Programming
KEY & TOUCH
ACCELEROMETER
GRAVITY
GYROSCOPE
LIGHT
MAGNETIC
ORIENTATION
LOCATION
PRESSURE
Parallel tasksConcurrently executing tasks
멀티 태스크
Sequencial tasksOne after another
Task A
Request
Task B
Task C
Task A Task B Task C
Request
Result Result
반드시 순서대로 실행 되어
야 하는 태스크 묶음
흔한 로그인 시나리오
1. Auth
2. Token
3. Login
4. Result
5. Request
6. Image
Login with
순서대로 실행 되어야 함
순서를 갖는 작업들
Sequencial tasks
TASK A TASK B TASK CBackground
UI Thread Req Resp
Callback 만을 지원하는 라이브러리 사용시
Sequencial tasks
TASK A TASK B TASK CBackground
UI Thread Req Resp
불필요한 UI 쓰레드 점유특히, Volley
구현 방식
Sequencial tasks
• Nested Callbacks
• Future combination
• SingleThreadExecutor
• Handler + Looper
Callback hell을 해결 할 수 있지만,
소스 네비게이션시 라인 점프는 늘어 날 수 있음.
하나의 쓰레드와 Task를 결합. 단, 오류 처리는 쉽지 않음
직접 워커를 만듦 커스텀 콜백 인터페이스를 정의 해야 함
Callback hell
흔한 로그인 시나리오
requestFacebookLogin(new Callback() { public void onSuccess(Result result) { ... } });
Callback hell
흔한 로그인 시나리오
requestFacebookLogin(new Callback() { public void onSuccess(Result result) { String facebookId = result.getFacebookId(); requestLogin(facebookId, new Callback() { ... }); } });
Callback hell
흔한 로그인 시나리오
requestFacebookLogin(new Callback() { public void onSuccess(Result result) { String facebookId = result.getFacebookId(); requestLogin(facebookId, new Callback() { public void onSuccess(Result result) { result.getUserDetails(new Callback() { @Override public void onSuccess(User user) { ... } }); } }); } });
예외 처리 추가시 분기문 추가시 depth가 2배씩 증가;;
재시도 전략
흔한 로그인 시나리오
requestFacebookLogin(new Callback() { public void onSuccess(Result result) { String facebookId = result.getFacebookId(); requestLogin(facebookId, new Callback() { public void onSuccess(Result result) { result.getUserDetails(new Callback() { @Override public void onSuccess(User user) { ... } }); } public void onFail(Result result){ retry( ??? ); } }); } });
각각 Task에 예외가 발생한 작업을 재시도 하고 싶을 땐?
@#!%
페이스북 로그인 > 앱 서버 로그인 > 초기 데이터 요청 > 오류 처리
흔한 로그인 시나리오
requestFacebookLogin(new Callback() { public void onSuccess(Result result) { String facebookId = result.getFacebookId(); requestLogin(facebookId, new Callback() { public void onSuccess(Result result) { result.getUserDetails(new Callback() { @Override public void onSuccess(User user) { ... } }); } public void onFail(Result result){ retry( ??? ); showToast(context, R.String.loginFail); } }); } });
UI Thread를 통해 유저에게 알리고 싶은데,
“나는 누구 여긴 어디?”
Parallel tasksConcurrently executing tasks
멀티 태스크
Sequencial tasksOne after another
Task A
Request
Task B
Task C
Task A Task B Task C
Request
Result Result
태스크간의 의존성이 없어, 동시
에 실행 될 수 있음.
그러나, 우리는 UI 프로그래머
흔한 컨텐츠 공유 시나리오
1. Req
1. Req
1. Req
1. Req
흔한 컨텐츠 공유 시나리오
1. Req
2. Resp
1. Req
2. Resp
1. Req
2. Resp
1. Req
2. Resp
흔한 컨텐츠 공유 시나리오
동시에 수행 해야 하는 작업들
Parellel Tasks
DataThread A
UI Thread Login Data
latency
Thread B
Thread C
Data latency
Data latency
작업이 끝나는 시간은 모두 다를 수 있음.
모든 작업이 끝났을 때, UI에 반영하고 싶은데…
1. Req
2. Resp
1. Req
2. Resp
1. Req
2. Resp
1. Req
2. Resp
작업이 완료
되었습니다.
흔한 컨텐츠 공유 시나리오
구현 방식
Parellel Tasks
• Creating Multiple Threads
• CountDownLatch, MultiThreadExecutor (Thread Pool)
각각의 태스크를 수행하고 flag를 통해 결과를 취합
요청이 많은 경우 쓰레드 풀을 이용
concurrent 패키지의 CountDownLatch
순차 + 병렬
Composed Tasks
Task A
Request
Task B
Task C
Task A Task B Task B
Result
앱을 통해 사진과 글을 포스팅
(순차 태스크)포스팅한 글을 SNS에 공유
(병렬 태스크)
어떤 데이터가 있는데…
어떤 쓰레드에서 수행하고,
필요한 데이터만 걸러내고,
데이터를 어떻게 변환하고,
오류 발생시에는 어떻게 대응할지,
어떤 쓰레드에서 결과를 받을지,
시작하면,
!
필요한 경우 취소 할 수 있어야 한다.
!
테스트도 쉬워야겠고!
유추 가능한 것은 생략 할 수 있으면 좋겠다.
이런거 없나?
observable
어떤 쓰레드에서 수행하고,
필요한 데이터만 걸러내고,
데이터를 어떻게 변환하고,
오류 발생시에는 어떻게 대응할지,
어떤 쓰레드에서 결과를 받을지,
시작하면,
!
필요한 경우 취소 할 수 있어야 한다.
!
테스트도 쉬워야겠고!
유추 가능한 것은 생략 할 수 있으면 좋겠다.
이런거 없나?
observable
.subscribeOn(background)
필요한 데이터만 걸러내고,
데이터를 어떻게 변환하고,
오류 발생시에는 어떻게 대응할지,
어떤 쓰레드에서 결과를 받을지,
시작하면,
!
필요한 경우 취소 할 수 있어야 한다.
!
테스트도 쉬워야겠고!
유추 가능한 것은 생략 할 수 있으면 좋겠다.
이런거 없나?
observable
.subscribeOn(background)
.filter(paidUser())
데이터를 어떻게 변환하고,
오류 발생시에는 어떻게 대응할지,
어떤 쓰레드에서 결과를 받을지,
시작하면,
!
필요한 경우 취소 할 수 있어야 한다.
!
테스트도 쉬워야겠고!
유추 가능한 것은 생략 할 수 있으면 좋겠다.
이런거 없나?
observable
.subscribeOn(background)
.filter(paidUser())
.map(stringToJson())
오류 발생시에는 어떻게 대응할지,
어떤 쓰레드에서 결과를 받을지,
시작하면,
!
필요한 경우 취소 할 수 있어야 한다.
!
테스트도 쉬워야겠고!
유추 가능한 것은 생략 할 수 있으면 좋겠다.
이런거 없나?
observable
.subscribeOn(background)
.filter(paidUser())
.map(stringToJson())
.doOnError(handleError())
어떤 쓰레드에서 결과를 받을지,
시작하면,
!
필요한 경우 취소 할 수 있어야 한다.
!
테스트도 쉬워야겠고!
유추 가능한 것은 생략 할 수 있으면 좋겠다.
이런거 없나?
observable
.subscribeOn(background)
.filter(paidUser())
.map(stringToJson())
.doOnError(handleError())
.observeOn(mainThread)
시작하면,
!
필요한 경우 취소 할 수 있어야 한다.
!
테스트도 쉬워야겠고!
유추 가능한 것은 생략 할 수 있으면 좋겠다.
이런거 없나?
observable
.subscribeOn(background)
.filter(paidUser())
.map(stringToJson())
.doOnError(handleError())
.observeOn(mainThread)
.subscribe() -> subscription
!
필요한 경우 취소 할 수 있어야 한다.
!
테스트도 쉬워야겠고!
유추 가능한 것은 생략 할 수 있으면 좋겠다.
이런거 없나?
observable
.subscribeOn(background)
.filter(paidUser())
.map(stringToJson())
.doOnError(handleError())
.observeOn(mainThread)
.subscribe() -> subscription
!
subscription.unsubscribe()
!
테스트도 쉬워야겠고!
유추 가능한 것은 생략 할 수 있으면 좋겠다.
이런거 없나?
observable
.subscribeOn(background)
.filter(paidUser())
.map(stringToJson())
.doOnError(handleError())
.observeOn(mainThread)
.subscribe() -> subscription
!
subscription.unsubscribe()
!
!
유추 가능한 것은 생략 할 수 있으면 좋겠다.
이런거 없나?
!
assert…(paidUser())
assert…(stringToJson())
assert…(handleError())
observable
.subscribeOn(background)
.filter(paidUser())
.map(stringToJson())
.doOnError(handleError())
.observeOn(mainThread)
.subscribe() -> subscription
!
subscription.unsubscribe()
!
!
이런거 없나?
RxJava
RxJava
로직에만 집중
테스트 용이성
가독성
이름짓기?
내가 작성하는 코드가 어떤 쓰레드에서 돌아갈지
지금 당장은 관심이 없어.
왜냐하면, 실제 사용 할 시점에서 지정하면 되니까.
모듈화가 쉽다 == 테스트하기 쉽다
메소드 체이닝, 왼쪽에서 오른쪽으로 읽자
코드가 하는 일은 코드 자체가 표현하도록 하자.
3초 동안 입력이 없으면 추천 검색어를 제시
Custom Events
Custom Events
D
3초 동안 입력이 없으면 추천 검색어를 제시
Custom Events
D E V
3초 동안 입력이 없으면 추천 검색어를 제시
Custom Events
D E V
1s
3초 동안 입력이 없으면 추천 검색어를 제시
Custom Events
D E V
1s 2s
3초 동안 입력이 없으면 추천 검색어를 제시
Custom Events
D E V
dev 검색 요청
1s 2s 3s
3초 동안 입력이 없으면 추천 검색어를 제시
Custom Events
D E V
dev 검색 요청
1s 2s 3s
3초 동안 입력이 없으면 추천 검색어를 제시
Custom Events
Custom Events
D E V
1s 2s 3s
Thread
lastInputTime 0 0 0 3
Callback
1. 쓰레드 만들고
2. 이벤트 발생 시간 기록
3. 마지막 이벤트와 현재 시간 비교
4. 설정 값 이상의 차이가 발생시 콜백
또는,
1. 수행할 태스크를 작성
2. 태스크 등록(postDelayed 사용)
3. 이벤트 발생시 등록 이벤트 취소
4. 등록 된 태스크가 수행되며 콜백
람다
안드로이드 스튜디오 사용시 코드 폴딩을 통해 간결하게 표현되는 놈?
람다
Runnable task = new Runnable(){ @Override public void run(){ System.out.println("Hello Lamba"); } };
Runnable task2 = () -> { System.out.println("Hello Lamba"); };
람다
Runnable task = new Runnable(){ @Override public void run(){ System.out.println("Hello Lamba"); } };
Runnable task2 = () -> { System.out.println("Hello Lamba"); };
인터페이스가 하나의 메소드를 호출하면
굳이 메소드의 이름을 적을 필요는 없으니 생략
람다
Runnable task = new Runnable(){ @Override public void run(){ System.out.println("Hello Lamba"); } };
Runnable task2 = () -> { System.out.println("Hello Lamba"); };
파라미터는 비었으니 빈 괄호를 넣어주자
람다
Runnable task = new Runnable(){ @Override public void run(){ System.out.println("Hello Lamba"); } };
Runnable task2 = () -> { System.out.println("Hello Lamba"); };
화살표 나오면 람다식!
블럭 내의 내용은 그대로~
이클립스에서 람다 사용
Xtend -> 자바 클래스로 변환
Xtend
• 안드로이드의 Swift
• 람다식은 익명 클래스를 통해 구현함
• 자바코드로 컴파일 되므로 호환성 문제가 없음
• 함수형 프로그래밍의 특징을 상당부분 수용함
• Eclipse IDE 지원
호환성 끝판왕
단점:
Eclipse에서 만들었음에도 리팩토링 기능이 취약함.
IntelliJ에서는 그냥 텍스트파일
프로젝트가 커질수록 컴파일 타임이 급격히 증가
빌드 프로세스를 고민 할 필요가 있음.
자체적으로 Guava 활용. 테스트 작성시 코드를
극히 간결하게 유지 할 수 있음.
메소드 단위 테스트가 불편
자바와 스칼라의 중간 어디쯤
안드로이드 스튜디오에서 람다 사용
바이트 코드 조작을 통한 람다 사용
RetroLambda
• 암묵적 참조로 클래스 참조로 인한 메모리 문제가 없음
스튜디오의 기본기능인 폴딩도 괜찮지만,
람다 사용이 조금 더 편함
함수형 프로그래밍
외부에서 건드릴 수 없음!
람다식이 함수형 프로그래밍은 절대 아님!
함수형 프로그래밍의 특징
(x ∧ z) ∨ (y ∧ z) = (x ∨ y) ∧ z불 대수
x, y z 는 숫자이거나 집합
함수형 프로그래밍의 특징
(λx.(λx.x)) y # λx.y
불 대수
람다 대수
(x ∧ z) ∨ (y ∧ z) = (x ∨ y) ∧ z
클래스가 함수라면! 람다 대수에 의해 연산 가능!
Circle Progress
RxJava
Callback => Observable
onDraw(Canvas canvas)
Observer<Canvas>
RxJava
Subscriber<? super Canvas> canvasSubscriber; !@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvasSubscriber.onNext(canvas); }
Callback => Observable
RxJava를 통해 콜백을 마치 컬렉션 처럼 바꿔줌
RxJava
Callback => Observable
Observable
onDraw(Canvas canvas)
Observer<Canvas>
이제 collection 처럼 사용가능
60fps
RxJava
Callback => Observable
ObservableObserver<Canvas>
getDataFromLocalMemory() .skip(10) .take(5) .filter(...) .map(s -> s + " mapped" ) .forEach( print(s) )
getDataFromNetwork() .skip(10) .take(5) .filter(...) .map(s -> s + " mapped" ) .subscribe( print(s) )
Java 8’s Stream RxJava’s Observable
함수형 특징을 가진 언어는 다들 비슷비슷
Callback => Observable
RxJava
sineValueObservable
drawCircle()Zip
canvasObservable
지퍼 맞음
Callback => Observable
RxJava
sineValueObservable
drawCircle()Zip
canvasObservable
지퍼 맞음
Callback => Observable
RxJava
sineValueObservable
drawCircle()Zip
canvasObservable
지퍼 맞음
RxJava
Callback => Observable
canvasObservable
sineValueObservable
drawCircle()Zip
두 collection(obsavable)의
각각 요소를 하나씩 결합!
짝이 성사되면 그리기 수행!
RxJava
Callback => Observable
canvasObservable
sineValueObservable
drawCircle()Zip
결과물 역시 Obserable
이므로, 추가적인 연산이
얼만든지 가능함~
왜 RxJava?
왜 RxJava?
Reactive Extension
ReactiveX
RxJs의 형제
너무 어려워요…
함수형 프로그래밍은 어려운게 아닙니다.
!익숙해지는데 시간이 좀 걸릴 뿐입니다.
!Rx와 함수형이 만능은 아닙니다.
!칼퇴근을 보장하진 못하지만,
!품질은 높아지고, 스트레스는 낮아집니다.
!어렵게 느껴지면, 금요일 라 스칼라 코딩단에 구경오세요
!같이 고민해봐요.
! …안드로이드 개발자 항시 대기
!
RxJAVA?!
1. RxJava란?!2. RxJava(Thread, 이벤트 합성, 시간 관리, Subject, 예외 관리)!3. RxJava With MVVM!
RxJava
RxJava란?
Excel - 각 항목에 변화가 있으면 자동으로 감지해서 값을 갱신해준다.
!!
A
B
C
1 2
10 20
= A1+A230
RxJava란?
Excel - 각 항목에 변화가 있으면 자동으로 감지해서 값을 갱신해준다.
!!
A
B
C
1 2
10
= A1+A230
RxJava란?
Excel - 각 항목에 변화가 있으면 자동으로 감지해서 값을 갱신해준다.
!!
A
B
C
1 2
10
= A1+A230
30
RxJava란?
Excel - 각 항목에 변화가 있으면 자동으로 감지해서 값을 갱신해준다.
!!
A
B
C
1 2
10
= A1+A2
30
RxJava란?
Excel - 각 항목에 변화가 있으면 자동으로 감지해서 값을 갱신해준다.
!!
A
B
C
1 2
10
= A1+A2
30
40
Observable - 관측 가능한 값의 흐름를 나타내는 객체 (생산자)
Observer - 값의 흐름를 받는 객체 (소비자)!
RxJava - Observable, Observer란?
Button
Network
Keyboard
Alert
Image View
Text View
Image
Error
Input Text
Observable Observer
String
Byte[]
Click String
Visibility
ImageEvent
Observable - 관측 가능한 값의 흐름를 나타내는 객체 (생산자) - subscribe 함수를 통해 구독이 가능하며, 구독자에게 값을 흐름을 알려준다.
!
Observer - 값의 흐름를 받는 객체 (소비자) - Observable의 값의 흐름에 따라 onNext, onError, onCompelete를 호출 받는다.
!
RxJava - Observable, Observer란?
OnNext OnComplete
OnError
RxJava - Observable, Observer란?
eventSource.subscribe((event)-> { // some action. } );
Observable
Observer
subscribe / unsubcribe - subscribe 를 통하여 Observable의 값의 변화를 구독하고,
unsubscribe를 통해 구독을 해지한다.
!!
RxJava - subscribe/unsubscribe
Observable!(Operation!
or!Producer)
Observable!(Operation) Observer
Button Function View
subscribe / unsubcribe - subscribe 를 통하여 Observable의 값의 변화를 구독하고,
unsubscribe를 통해 구독을 해지한다.
!!
RxJava - subscribe/unsubscribe
Observable!(Operation!
or!Producer)
Observable!(Operation) Observer
subscribe()onSubscribe()Button Function View
subscribe / unsubcribe - subscribe 를 통하여 Observable의 값의 변화를 구독하고,
unsubscribe를 통해 구독을 해지한다.
!!
RxJava - subscribe/unsubscribe
Observable!(Operation!
or!Producer)
Observable!(Operation) Observer
subscribe()onSubscribe()
onNext() onNext()
Button Function View
subscribe / unsubcribe - subscribe 를 통하여 Observable의 값의 변화를 구독하고,
unsubscribe를 통해 구독을 해지한다.
!!
RxJava - subscribe/unsubscribe
Observable!(Operation!
or!Producer)
Observable!(Operation) Observer
subscribe()onSubscribe()
onNext() onNext(). . .
. . .
Button Function View
subscribe / unsubcribe - subscribe 를 통하여 Observable의 값의 변화를 구독하고,
unsubscribe를 통해 구독을 해지한다.
!!
RxJava - subscribe/unsubscribe
Observable!(Operation!
or!Producer)
Observable!(Operation) Observer
subscribe()onSubscribe()
onNext() onNext()
onNext() onNext()
. . .. . .
Button Function View
subscribe / unsubcribe - subscribe 를 통하여 Observable의 값의 변화를 구독하고,
unsubscribe를 통해 구독을 해지한다.
!!
RxJava - subscribe/unsubscribe
Observable!(Operation!
or!Producer)
Observable!(Operation) Observer
subscribe()onSubscribe()
onNext() onNext()
onNext() onNext()
onComplete() onComplete()
. . .. . .
Button Function View
subscribe / unsubcribe - subscribe 를 통하여 Observable의 값의 변화를 구독하고,
unsubscribe를 통해 구독을 해지한다.
!!
RxJava - subscribe/unsubscribe
Observable!(Operation!
or!Producer)
Observable!(Operation) Observer
subscribe()onSubscribe()
onNext() onNext()
onNext() onNext()
onComplete() onComplete()
unsubscribe()unsubscribe()
. . .. . .
Button Function View
키보드 입력값 출력 시나리오 - 키를 입력하면 TextView에 출력하는 시나리오
RxJava - map
EditText
Function
TextView
키보드 입력값 출력 시나리오 - 키를 입력하면 TextView에 출력하는 시나리오
RxJava - map
EditText
Function
TextView
Text A
fun(x)
TextA-1
키보드 입력값 출력 시나리오 - 키를 입력하면 TextView에 출력하는 시나리오
RxJava - map
EditText
Function
TextView
Text B
fun(x)
TextB-1
Text A
fun(x)
TextA-1
키보드 입력값 출력 시나리오 - 키를 입력하면 TextView에 출력하는 시나리오
RxJava - map
EditText
Function
TextView
Text B
fun(x)
TextB-1
Text C
fun(x)
TextC-1
Text A
fun(x)
TextA-1
map - 전달되는 값에 함수를 적용해서 새로운 값을 만들어냄
!!
RxJava - map
eventSource.map((value) -> {return fun(value);}) .subscribe((result) -> { ! // show result. });
키보드 입력값 출력 시나리오 - 키를 입력하면 추천 검색어를 서버에서 받아와 출력하는 시나리오
RxJava - flatMap
EditText
Async Function
TextView
키보드 입력값 출력 시나리오 - 키를 입력하면 추천 검색어를 서버에서 받아와 출력하는 시나리오
RxJava - flatMap
EditText
Async Function
TextView
Text A
키보드 입력값 출력 시나리오 - 키를 입력하면 추천 검색어를 서버에서 받아와 출력하는 시나리오
RxJava - flatMap
EditText
Async Function
TextView
fun(x)
Text A
키보드 입력값 출력 시나리오 - 키를 입력하면 추천 검색어를 서버에서 받아와 출력하는 시나리오
RxJava - flatMap
EditText
Async Function
TextView
fun(x)
TextA-1
Text A
키보드 입력값 출력 시나리오 - 키를 입력하면 추천 검색어를 서버에서 받아와 출력하는 시나리오
RxJava - flatMap
EditText
Async Function
TextView
fun(x)
TextA-1
Text A Text B
키보드 입력값 출력 시나리오 - 키를 입력하면 추천 검색어를 서버에서 받아와 출력하는 시나리오
RxJava - flatMap
EditText
Async Function
TextView
fun(x)
TextA-1
Text A
fun(x)
Text B
키보드 입력값 출력 시나리오 - 키를 입력하면 추천 검색어를 서버에서 받아와 출력하는 시나리오
RxJava - flatMap
EditText
Async Function
TextView
fun(x)
TextA-1
Text A
fun(x)
TextB-1
Text B
키보드 입력값 출력 시나리오 - 키를 입력하면 추천 검색어를 서버에서 받아와 출력하는 시나리오
RxJava - flatMap
EditText
Async Function
TextView
fun(x)
TextA-1
Text A
fun(x)
TextB-1
Text B Text C
키보드 입력값 출력 시나리오 - 키를 입력하면 추천 검색어를 서버에서 받아와 출력하는 시나리오
RxJava - flatMap
EditText
Async Function
TextView
fun(x)
TextA-1
Text A
fun(x)
TextB-1
Text B
fun(x)
Text C
키보드 입력값 출력 시나리오 - 키를 입력하면 추천 검색어를 서버에서 받아와 출력하는 시나리오
RxJava - flatMap
EditText
Async Function
TextView
fun(x)
TextA-1
Text A
fun(x)
TextB-1
Text B
fun(x)
TextC-1
Text C
flatmap - 미래에 0개 ~ n개의 결과를 반환하는 함수를 적용한다.
!!
RxJava - flatMap
eventSource.flatMap((value) -> {return longlongFunc(value);}) .subscribe((Result) -> { ! // show result. ! });
Observable
Thread 관리
이미지 다운로드 시나리오 - 버튼을 클릭하면 이미지를 다운받아 표시하는 예제
RxJava - Scheduler
UI Thread
IO Thread
UI Thread
Click
Image
Image
subscribeOn - 비동기 결과값을 가져오는 쓰레드를 결정한다
observeOn - 결과 값을 처리할 쓰레드를 결정한다.
RxJava - Scheduler
ClickEvents.flatMap((event) -> { return asyncDownloadImage(params) .subscribeOn(Schedulers.io()); }) .observeOn(AndroidSchedulers.mainThread()) .subscribe((image) -> { // show result. });
observeOn - 결과 값을 처리할 쓰레드를 결정한다.
RxJava - Scheduler
Event
observeOn(MainThreadScheduler)
Main Thread
Thread
subscribeOn - 비동기 결과값을 가져오는 쓰레드를 결정한다.
RxJava - Scheduler
subscribeOn(IOThreadScheduler)
IO Thread Event
IO Thread
Schedulers.computation() - 단순 연산을 처리하는 Thread들의 Pool로 구성된 Scheduler.
!
Schedulers.io() - IO처리를 담당하는 Thread들의 Pool로 구성된 Scheduler.
- 필요에 따라 자동으로 Pool의 크기를 늘리거나 줄인다.
!
Schedulers.newThread() - 새로운 쓰레드를 만드는 Scheduler.
RxJava - Scheduler
AndroidSchedulers.mainThread( ) - Android의 Main Thread에서 작업을 수행한다.
!
AndroidSchedulers.handlerThread(Handler handler) - 특정 Thread를 사용하는 Handler이용하는 Scheduler를 생성한다.
!
Schedulers.from(Executor executor) - 특정 Executor를 사용하는 Scheduler를 생성한다.
!!
RxJava - Scheduler
이벤트 합성
combineLatest - Observable 들의 이벤트들 중 가장 최신 이벤트 값을 합성해서 사용한다.
!
RxJava - combineLatest, merge
Observable.combineLatest(color, shape, new Func2<Color, Shape, Image>() { @Override public Image call(Color color, Shape shape) { return image.createFrom(color, shape); } } );
combineLatest - Observable 들의 중 가장 최신의 이벤트 값을 합성한다.
!
RxJava - combineLatest, merge
Image
combineLatest( )Color
Shape
combineLatest - Observable 들의 중 가장 최신의 이벤트 값을 합성한다.
!
RxJava - combineLatest, merge
1
Image
combineLatest( )Color
Shape
combineLatest - Observable 들의 중 가장 최신의 이벤트 값을 합성한다.
!
RxJava - combineLatest, merge
1
Image
combineLatest( )Color
Shape
2
2
combineLatest - Observable 들의 중 가장 최신의 이벤트 값을 합성한다.
!
RxJava - combineLatest, merge
1
Image
combineLatest( )Color
Shape
2
2
3
3
combineLatest - Observable 들의 중 가장 최신의 이벤트 값을 합성한다.
!
RxJava - combineLatest, merge
1
Image
combineLatest( )Color
Shape
2
2
3
3 4
4
merge - Observable 들의 이벤트들을 합성해서 사용한다.
!
RxJava - combineLatest, merge
Observable<Void> clickEvnets = Observable.merge(clickEventFromButtonA, clickEventFromButtonB);
merge - Observable 들의 이벤트들을 합성해서 사용한다.
!
RxJava - combineLatest, merge
Login Event
merge( )Login Button
Enter Key
merge - Observable 들의 이벤트들을 합성해서 사용한다.
!
RxJava - combineLatest, merge
Login Event
1
1
merge( )Login Button
Enter Key
merge - Observable 들의 이벤트들을 합성해서 사용한다.
!
RxJava - combineLatest, merge
Login Event
1
1
merge( )Login Button
Enter Key
2
2
merge - Observable 들의 이벤트들을 합성해서 사용한다.
!
RxJava - combineLatest, merge
Login Event
1
1
merge( )Login Button
Enter Key
3
32
2
merge - Observable 들의 이벤트들을 합성해서 사용한다.
!
RxJava - combineLatest, merge
Login Event
1
1
merge( )Login Button
Enter Key
3
32
2 4
4
시간 관리
예제) throttleWithTimeout - 자동 완성 기능
!
RxJava - Time
// 기본 변수 설정 final PublishSubject<String> inputText = PublishSubject.create(); TextView autoCompleteTextView = (TextView) findViewById(R.id.auto_complete_text); !// 1초동안 사용자 입력이 없는 경우 네트워크에서 값을 받아와서 갱신 inputText.throttleWithTimeout(1, TimeUnit.SECONDS) .observeOn(Schedulers.io()) .flatMap(new GetAutocompleteKeywordsFromNetwork()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new SetTextAction(autoCompleteTextView));
delay - 모든 이벤트를 주어진 시간만큼 지연한다.
!
RxJava - Time
Observer
delay( )
delay - 모든 이벤트를 주어진 시간만큼 지연한다.
!
RxJava - Time
1
Observer
delay( )
delay - 모든 이벤트를 주어진 시간만큼 지연한다.
!
RxJava - Time
1
Observer 1
delay( )
delay - 모든 이벤트를 주어진 시간만큼 지연한다.
!
RxJava - Time
21
Observer 1
delay( )
delay - 모든 이벤트를 주어진 시간만큼 지연한다.
!
RxJava - Time
21
Observer 1 2
delay( )
delay - 모든 이벤트를 주어진 시간만큼 지연한다.
!
RxJava - Time
21 3
Observer 1 2
delay( )
delay - 모든 이벤트를 주어진 시간만큼 지연한다.
!
RxJava - Time
21 3
Observer 1 2 3
delay( )
throttleFirst - 한 이벤트 후의 일정 시간만큼의 이벤트를 무시한다.
!
RxJava - Time
Observer
throttleFirst( )
throttleFirst - 한 이벤트 후의 일정 시간만큼의 이벤트를 무시한다.
!
RxJava - Time
Observer
throttleFirst( )
1
1
throttleFirst - 한 이벤트 후의 일정 시간만큼의 이벤트를 무시한다.
!
RxJava - Time
Observer
throttleFirst( )
1
1
throttleFirst - 한 이벤트 후의 일정 시간만큼의 이벤트를 무시한다.
!
RxJava - Time
Observer
throttleFirst( )
1
1 2
throttleFirst - 한 이벤트 후의 일정 시간만큼의 이벤트를 무시한다.
!
RxJava - Time
Observer
throttleFirst( )
1
1
3
32
throttleFirst - 한 이벤트 후의 일정 시간만큼의 이벤트를 무시한다.
!
RxJava - Time
Observer
throttleFirst( )
1
1
3
32
throttleFirst - 한 이벤트 후의 일정 시간만큼의 이벤트를 무시한다.
!
RxJava - Time
Observer
throttleFirst( )
1
1
3
32 4
throttleFirst - 한 이벤트 후의 일정 시간만큼의 이벤트를 무시한다.
!
RxJava - Time
Observer
throttleFirst( )
1
1
3
3 5
5
2 4
throttleFirst - 한 이벤트 후의 일정 시간만큼의 이벤트를 무시한다.
!
RxJava - Time
Observer
throttleFirst( )
1
1
3
3 5
5
2 4
throttleWithTimeout - 일정 시간만큼의 이벤트가 없는 경우에 가장 마지막 이벤트를 전달한다.
!
RxJava - Time
Observer
throttleWithTimeout( )
throttleWithTimeout - 일정 시간만큼의 이벤트가 없는 경우에 가장 마지막 이벤트를 전달한다.
!
RxJava - Time
Observer
throttleWithTimeout( )
1
throttleWithTimeout - 일정 시간만큼의 이벤트가 없는 경우에 가장 마지막 이벤트를 전달한다.
!
RxJava - Time
Observer
throttleWithTimeout( )
1
throttleWithTimeout - 일정 시간만큼의 이벤트가 없는 경우에 가장 마지막 이벤트를 전달한다.
!
RxJava - Time
Observer
throttleWithTimeout( )
1
1
throttleWithTimeout - 일정 시간만큼의 이벤트가 없는 경우에 가장 마지막 이벤트를 전달한다.
!
RxJava - Time
Observer
throttleWithTimeout( )
1 2
1
throttleWithTimeout - 일정 시간만큼의 이벤트가 없는 경우에 가장 마지막 이벤트를 전달한다.
!
RxJava - Time
Observer
throttleWithTimeout( )
1 2
1
throttleWithTimeout - 일정 시간만큼의 이벤트가 없는 경우에 가장 마지막 이벤트를 전달한다.
!
RxJava - Time
Observer
throttleWithTimeout( )
1 2 3
1
throttleWithTimeout - 일정 시간만큼의 이벤트가 없는 경우에 가장 마지막 이벤트를 전달한다.
!
RxJava - Time
Observer
throttleWithTimeout( )
1 2 3
1
throttleWithTimeout - 일정 시간만큼의 이벤트가 없는 경우에 가장 마지막 이벤트를 전달한다.
!
RxJava - Time
Observer
throttleWithTimeout( )
1 2 43
1
throttleWithTimeout - 일정 시간만큼의 이벤트가 없는 경우에 가장 마지막 이벤트를 전달한다.
!
RxJava - Time
Observer
throttleWithTimeout( )
1 2 43
1
throttleWithTimeout - 일정 시간만큼의 이벤트가 없는 경우에 가장 마지막 이벤트를 전달한다.
!
RxJava - Time
Observer
throttleWithTimeout( )
1 2 43
1 4
Subject
BehaviorSubject를 사용한 SharedPreferences 예제!!
RxJava - Subject(Observable + Observer)
final String KEY = “key_user_id”; ! // User ID 변수 선언 BehaviorSubject<String> userId = BehaviorSubject.create(); ! // SharePreference 에서 Background Thread로 값을 읽어온다. readFromSharedPreferences(KEY, "").subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(userId); ! // userId 값이 변경되면 SharePreference에 비동기로 값을 기록한다. userId.distinctUntilChanged() .observeOn(Schedulers.io()) .subscribe(new WriteToSharedPreferences(KEY));
BehaviorSubject!!!
RxJava - Subject(Observable + Observer)
BehaviorSubject
Observer1
Observer2
BehaviorSubject!!!
RxJava - Subject(Observable + Observer)
1
BehaviorSubject
Observer1
Observer2
BehaviorSubject!!!
RxJava - Subject(Observable + Observer)
1su
bscribe()
BehaviorSubject
Observer1
Observer2
BehaviorSubject!!!
RxJava - Subject(Observable + Observer)
1su
bscribe()
1
BehaviorSubject
Observer1
Observer2
BehaviorSubject!!!
RxJava - Subject(Observable + Observer)
2
21su
bscribe()
1
BehaviorSubject
Observer1
Observer2
BehaviorSubject!!!
RxJava - Subject(Observable + Observer)
2
21su
bscribe()
1
subs
cribe()
BehaviorSubject
Observer1
Observer2
BehaviorSubject!!!
RxJava - Subject(Observable + Observer)
2
21su
bscribe()
1
2
subs
cribe()
BehaviorSubject
Observer1
Observer2
BehaviorSubject!!!
RxJava - Subject(Observable + Observer)
2
21su
bscribe()
1
2
3
3
3
subs
cribe()
BehaviorSubject
Observer1
Observer2
PublishSubject!!
RxJava - Subject(Observable + Observer)
PublishSubject
Observer1
Observer2
PublishSubject!!
RxJava - Subject(Observable + Observer)
1
PublishSubject
Observer1
Observer2
PublishSubject!!
RxJava - Subject(Observable + Observer)
1su
bscribe()
PublishSubject
Observer1
Observer2
PublishSubject!!
RxJava - Subject(Observable + Observer)
2
21su
bscribe()
PublishSubject
Observer1
Observer2
PublishSubject!!
RxJava - Subject(Observable + Observer)
2
21su
bscribe()
subs
cribe()
PublishSubject
Observer1
Observer2
PublishSubject!!
RxJava - Subject(Observable + Observer)
2
21su
bscribe()
3
3
3
subs
cribe()
PublishSubject
Observer1
Observer2
Replay Subject!!
RxJava - Subject(Observable + Observer)
ReplaySubject
Observer1
Observer2
Replay Subject!!
RxJava - Subject(Observable + Observer)
1
ReplaySubject
Observer1
Observer2
Replay Subject!!
RxJava - Subject(Observable + Observer)
1su
bscribe()
ReplaySubject
Observer1
Observer2
Replay Subject!!
RxJava - Subject(Observable + Observer)
1su
bscribe()
ReplaySubject
Observer1
Observer21
Replay Subject!!
RxJava - Subject(Observable + Observer)
2
21su
bscribe()
ReplaySubject
Observer1
Observer21
Replay Subject!!
RxJava - Subject(Observable + Observer)
2
21su
bscribe()
subs
cribe()
ReplaySubject
Observer1
Observer21
Replay Subject!!
RxJava - Subject(Observable + Observer)
2
21su
bscribe()
subs
cribe()
ReplaySubject
Observer1
Observer21
1
Replay Subject!!
RxJava - Subject(Observable + Observer)
2
21su
bscribe()
subs
cribe()
ReplaySubject
Observer1
Observer21
1 2
Replay Subject!!
RxJava - Subject(Observable + Observer)
2
21su
bscribe()
subs
cribe()
ReplaySubject
Observer1
Observer21
1 2
3
3
3
예외 관리
doOnError - 실패한 경우, 예외를 처리하는 기능을 수행할 수 있습니다.
!
RxJava - Error Handling
GetDataFromNetwork() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnError((e) -> {showError(e);}) .subscribe((img) -> {showImage(img);})
1Observer
1 X
doOnError - 실패한 경우, 예외를 처리하는 기능을 수행할 수 있습니다.
!
RxJava - Error Handling
GetDataFromNetwork() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .doOnError((e) -> {showError(e);}) .subscribe((img) -> {showImage(img);})
1Observer
1 X
Error Handling
retry - 실패한 경우 자동으로 재시도 합니다.
!
RxJava - Error Handling
GetDataFromNetwork() .subscribeOn(Schedulers.io()) .retry()
Observer
retry()
retry - 실패한 경우 자동으로 재시도 합니다.
!
RxJava - Error Handling
GetDataFromNetwork() .subscribeOn(Schedulers.io()) .retry()
Observer
retry()1
1
retry - 실패한 경우 자동으로 재시도 합니다.
!
RxJava - Error Handling
GetDataFromNetwork() .subscribeOn(Schedulers.io()) .retry()
Observer
retry()1
1 X
retry - 실패한 경우 자동으로 재시도 합니다.
!
RxJava - Error Handling
GetDataFromNetwork() .subscribeOn(Schedulers.io()) .retry()
Observer
retry()1
1 X
retry - 실패한 경우 자동으로 재시도 합니다.
!
RxJava - Error Handling
GetDataFromNetwork() .subscribeOn(Schedulers.io()) .retry()
Observer
retry()1
1 X
3
3
retry(int n) - 실패한 경우 자동으로 n회 재시도 합니다.
!
RxJava - Error Handling
GetDataFromNetwork() .subscribeOn(Schedulers.io()) .retry(1)
Observer
retry(1)
retry(int n) - 실패한 경우 자동으로 n회 재시도 합니다.
!
RxJava - Error Handling
GetDataFromNetwork() .subscribeOn(Schedulers.io()) .retry(1)
Observer
retry(1)1
1
retry(int n) - 실패한 경우 자동으로 n회 재시도 합니다.
!
RxJava - Error Handling
GetDataFromNetwork() .subscribeOn(Schedulers.io()) .retry(1)
Observer
retry(1)1
1 X
retry(int n) - 실패한 경우 자동으로 n회 재시도 합니다.
!
RxJava - Error Handling
GetDataFromNetwork() .subscribeOn(Schedulers.io()) .retry(1)
Observer
retry(1)1
1 X
retry(int n) - 실패한 경우 자동으로 n회 재시도 합니다.
!
RxJava - Error Handling
GetDataFromNetwork() .subscribeOn(Schedulers.io()) .retry(1)
Observer
retry(1)1
1 X
2
2
retry(int n) - 실패한 경우 자동으로 n회 재시도 합니다.
!
RxJava - Error Handling
GetDataFromNetwork() .subscribeOn(Schedulers.io()) .retry(1)
Observer
retry(1)1
1 X
2
2 X
X
retryWhen(Observable) - 실패한 경우 자동으로 재시도 합니다.
!
RxJava - Error Handling
Observer
retryWhen()
retryWhen(Observable) - 실패한 경우 자동으로 재시도 합니다.
!
RxJava - Error Handling
Observer
retryWhen()
1
1
retryWhen(Observable) - 실패한 경우 자동으로 재시도 합니다.
!
RxJava - Error Handling
Observer
retryWhen()
1
1 X
retryWhen(Observable) - 실패한 경우 자동으로 재시도 합니다.
!
RxJava - Error Handling
Observer
retryWhen()
1
1 X
delay(3, SECODS)
retryWhen(Observable) - 실패한 경우 자동으로 재시도 합니다.
!
RxJava - Error Handling
Observer
retryWhen()
1
1 X
delay(3, SECODS)
retryWhen(Observable) - 실패한 경우 자동으로 재시도 합니다.
!
RxJava - Error Handling
Observer
retryWhen()
1
1 X
3
3
delay(3, SECODS)
retryWhen(Observable) - 실패한 경우 자동으로 재시도 합니다.
!
RxJava - Error Handling
Observer
retryWhen()
1
1 X
3
3
delay(3, SECODS)
!GetDataFromNetwork() .subscribeOn(Schedulers.io()) .retryWhen((observable) -> { // 3초마다 재시도 return observable.delay(3, TimeUnit.SECONDS); })
MVVM With RxJava
Loading Indicator 예제버튼을 클릭하면 다운로드가 완료될 때까지 Loading indicator를 표시한다.
!
RxJava - MVVM
! // 버튼 클릭 이벤트 Observable<Void> btnClickEvent = … ! Observable.merge( btnClickEvent.map((event) -> {return true;}, btnClickEvent.flatMap((event)-> { return downloadFunction().subscribeOn(Schedulers.io()) }).map((event) -> {return false;}) .observeOn(AndroidSchedulers.main()) ! .subscribe((isVisible) -> { v.setVisibility(isVisible ? View.VISIBLE : View.GONE); )}
Loading Indicator 예제버튼을 클릭하면 다운로드가 완료될 때까지 Loading indicator를 표시한다.
!
RxJava - MVVM
! // 버튼 클릭 이벤트 Observable<Void> btnClickEvent = … ! Observable.merge( btnClickEvent.map((event) -> {return true;}, btnClickEvent.flatMap((event)-> { return downloadFunction().subscribeOn(Schedulers.io()) }).map((event) -> {return false;}) .observeOn(AndroidSchedulers.main()) ! .subscribe((isVisible) -> { v.setVisibility(isVisible ? View.VISIBLE : View.GONE); )}
View Model
View
RxJava - MVVM
User Info
Login Info
Profile Image
Alert
Image View
Text View
Image
Error
Input Text
Model View
Image
String
String String
Visibility
Image
Event
View Model
운영체제에
의존적운영체제에
독립적
RxJava - MVVM
User Info
Login Info
Profile Image
Alert
Image View
Text View
Image
Error
Input Text
Model View
Image
String
String String
Visibility
Image
Event
View Model
재사용이
어려움재사용이
원활함
RxJava - MVVM
User Info
Login Info
Profile Image
Alert
Image View
Text View
Image
Error
Input Text
Model View
Image
String
String String
Visibility
Image
Event
View Model
테스트 불
가능 코드테스트 가능 코드
연결 코드
결론
프로그램의 일관성을 유지하는 통일된 방식이 필요 - Event, Thread, 시간, Exception을 처리하기에는 Callback은 조합이 어렵다.
- 그러므로 개개인이 각각의 방식으로 조합하여 복잡성이 증대된다.
!!
단순 객체지향으로는 큰 프로그램을 만들기 어렵다. - 객체지향은 내부의 상세한 구현을 숨긴다.
- 이로인해 잘 만들지 않으면 기대와 다른 동작들로 인해 복잡도가 증가한다.
- 이를 해결하기 위해 객체가 객체를 감싸고 또다시 감싸는 등의 문제가 발생한다.
!!
왜 Reactive Programming 인가?
프로그램의 일관성을 유지하는 통일된 방식이 필요 - Event, Thread, 시간, Exception을 처리하기에는 Callback은 조합이 어렵다.
- 그러므로 개개인이 각각의 방식으로 조합하여 복잡성이 증대된다.
!!
단순 객체지향으로는 큰 프로그램을 만들기 어렵다. - 객체지향은 내부의 상세한 구현을 숨긴다.
- 이로인해 잘 만들지 않으면 기대와 다른 동작들로 인해 복잡도가 증가한다.
- 이를 해결하기 위해 객체가 객체를 감싸고 또다시 감싸는 등의 문제가 발생한다.
!!
왜 Reactive Programming 인가?
통일된 방식으로 Event, Thread, Time, Exception을 처리할 수 있는 방법 제공
프로그램의 일관성을 유지하는 통일된 방식이 필요 - Event, Thread, 시간, Exception을 처리하기에는 Callback은 조합이 어렵다.
- 그러므로 개개인이 각각의 방식으로 조합하여 복잡성이 증대된다.
!!
단순 객체지향으로는 큰 프로그램을 만들기 어렵다. - 객체지향은 내부의 상세한 구현을 숨긴다.
- 이로인해 잘 만들지 않으면 기대와 다른 동작들로 인해 복잡도가 증가한다.
- 이를 해결하기 위해 객체가 객체를 감싸고 또다시 감싸는 등의 문제가 발생한다.
!!
왜 Reactive Programming 인가?
통일된 방식으로 Event, Thread, Time, Exception을 처리할 수 있는 방법 제공
외부에 제공할 수 있는 읽기 전용의 Observable을 제공하며 내용을 감추지
않아도 됨
보다 테스트하기 쉬운 구조가 필요하다. - 일반적인 구현에서는 가장 내부 깊은 곳에 View, Activity 등이 존재한다.
- 이로 인해 테스트에서 View를 제거하지 못해 테스트가 힘들어진다.
왜 Reactive Programming 인가?
보다 테스트하기 쉬운 구조가 필요하다. - 일반적인 구현에서는 가장 내부 깊은 곳에 View, Activity 등이 존재한다.
- 이로 인해 테스트에서 View를 제거하지 못해 테스트가 힘들어진다.
왜 Reactive Programming 인가?
MVVM의 구조를 활용하여 OS 종속적인 부분을 최대한 배제하고 테스트할 수 있음
감사합니다.