rxjava in looky

46
RxJava in Scott Joonhyeok Im for RxJava

Upload: joonhyeok-im

Post on 06-Apr-2017

52 views

Category:

Technology


2 download

TRANSCRIPT

RxJava in

Scott Joonhyeok Im

for RxJava

Why RxJava?

http://news20.busan.com/controller/newsController.jsp?newsId=20170123000265http://clien.net/cs2/bbs/board.php?bo_table=park&wr_id=53708884

2003 2017

An API for asynchronous programming with observable streams

Reactive Extensions a.k.a. ReactiveX

시작할까요? (y)

이미지 http://www.mediamolecule.com/blog/article/game_guide_go_go

asynchronousobservable

streams

비동기 asynchronous

동기 synchronous

“내가 지금 바쁘니까 있다가 줄께.” 요청과 결과가 동시에 일어나지 않음

“조금만 기다려. 되면 얼른 줄께.” 요청과 결과가 동시에 일어남

설계가 간단하고 직관적임 설계가 복잡함

결과가 주어질 때까지 아무것도 못하고 대기

아무리 길어져도 다른 작업을 할 수 있음 효율적인 자원관리가 가능

무거운 데이터 처리, 네트워크 I/O간단한 처리, 화면 처리

blocking non-blocking심화 https://slipp.net/questions/367

asynchronous observable

streams

이미지/참고 https://medium.com/@elliscmarte/introducing-reactive-programming-rx-js-fc7151a69508

Event Iterable (pull)

Observable (push)

Retrieve data T next() onNext(T)

Discover error throws Exception onError(Exception)

Complete !hasNext() onCompleted()

Single thread Single/Multi thread

Synchronous Asynchronous

asynchronous observable

streams

http://www.northstandchat.com/showthread.php?325439-Stream

$ pbpaste | highlight --syntax=java -O rtf --style molokai | pbcopy

InputStream fin = new FileInputStream("1209027132159_1.jpg"); OutputStream fos = new FileOutputStream("copy.jpg"); while(true) { int data = fin.read(); if(data == -1) { System.out.println("EOF"); break; } fos.write(data); } fin.close(); fos.close();

C:\> type autoexec.bat | more

그럼 이제 만들어봅시다!

이미지 http://mattswowguide.weebly.com/03-character-creation.html

Create Observable

Transform

Subscribe

Observable.create({ …

})

Create

Observable.just("only item")

Just From fromIterable, fromArray

List<String> list; Observable.fromIterable(list)

EditText editText; RxTextView.textChanges(editText)

Create Observable

Transform

Subscribe

Map Filter

FlatMap

Observable<List<String>> getTagRelations;

tagsObservable .flatMap(tag -> getTagRelations(tag))

생성 Create — 직접적인 코드 구현을 통해 옵저버 메서드를 호출하여 Observable을 생성한다 Defer — 옵저버가 구독하기 전까지는 Observable 생성을 지연하고 구독이 시작되면 옵저버 별로 새로운 Observable을 생성한다 Empty/Never/Throw — 아주 정확하고 제한된 행동을 하는 Observable을 생성한다 From — 다른 객체나 자료 구조를 Observable로 변환한다 Interval — 특정 시간별로 연속된 정수형을 배출하는 Observable을 생성한다 Just — 객체 하나 또는 객채집합을 Observable로 변환한다. 변환된 Observable은 원본 객체들을 발행한다 Range — 연속된 범위(Range)의 정수를 발행하는 Observable을 생성한다 Repeat — 특정 항목이나 연속된 항목들을 반복적으로 배출하는 Observable을 생성한다 Start — 함수의 실행 결과를 배출하는 Observable을 생성한다 Timer — 지정된 시간이 지나고 난 후 항목을 하나 배출하는 Observable을 생성한다

변환 Buffer — Observable로부터 정기적으로 항목들을 수집하고 묶음으로 만든 후에 묶음 안에 있는 항

목들을 한번에 하나씩 배출하지 않고 수집된 묶음 단위로 배출한다 FlatMap — 하나의 Observable이 발행하는 항목들을 여러개의 Observable로 변환하고, 항목들

의 배출을 차례차례 줄 세워 하나의 Observable로 전달한다 GroupBy — 원본 Observable이 배출하는 항목들을 키(Key) 별로 묶은 후 Observable에 담는

다. 이렇게 키 별로 만들어진 Observable들은 자기가 담고 있는 묶음의 항목들을 배출한다 Map — Observable이 배출한 항목에 함수를 적용한다

Scan — Observable이 배출한 항목에 연속적으로 함수를 적용하고 실행한 후 성공적으로 실행된 함수의 리턴 값을 발행한다

Window — 정기적으로 Observable의 항목들을 더 작은 단위의 Observable 윈도우로 나눈 후에, 한번에 하나씩 항목들을 발행하는 대신 작게 나눠진 윈도우 단위로 항목들을 배출한다

필터링 Debounce — Observable의 시간 흐름이 지속되는 상태에서 다른 항목들은 배출하지 않고 특정 시간 마다 그 시점에 존재하는 항목 하나를 Observable로부터 배출한다 Distinct — Observable이 배출하는 항목들 중 중복을 제거한 항목들을 배출한다 ElementAt — Obserable에서 n번째 항목만 배출한다 Filter — 테스트 조건을 만족하는 항목들만 배출한다 First — 맨 첫 번째 항목 또는 조건을 만족하는 첫 번째 항목만 배출한다 IgnoreElements — 항목들을 배출하지는 않고 종료 알림은 보낸다 Last — Observable의 마지막 항목만 배출한다 Sample — 특정 시간 간격으로 최근에 Observable이 배출한 항목들을 배출한다 Skip — Observable이 배출한 처음 n개의 항목들을 숨긴다 SkipLast — Observable이 배출한 마지막 n개의 항목들을 숨긴다 Take — Observable이 배츨한 처음 n개의 항목들만 배출한다 TakeLast — Observable이 배출한 마지막 n개의 항목들만 배출한다

결합 And/Then/When — 두 개 이상의 Observable들이 배출한 항목들을 'Pattern'과 'Plan' 중계자를 이용

해서 결합한다 CombineLatest — 두 개의 Observable 중 하나가 항목을 배출할 때 배출된 마지막 항목과 다른 한

Observable이 배출한 항목을 결합한 후 함수를 적용하여 실행 후 실행된 결과를 배출한다 Join — A Observable과 B Observable이 배출한 항목들을 결합하는데, 이때 B Observable은 배출한 항목이 타임 윈도우를 가지고 있고 이 타임 윈도우가 열린 동안 A Observable은 항목의 배출을 계속한다. Join 연산자는 B Observable의 항목을 배출하고 배출된 항목은 타임 윈도우를 시작시킨다. 타임 윈도우가 열려 있는 동안 A Observable은 자신의 항목들을 계속 배출하여 이 두 항목들을 결합한다 Merge — 복수 개의 Observable들이 배출하는 항목들을 머지시켜 하나의 Observable로 만든다

StartWith — 소스 Observable이 항목을 배출하기 전에 다른 항목들을 앞에 추가한 후 배출한다 Switch — Observable들을 배출하는 Observable을 싱글 Observable로 변환하다. 변환된 싱글

Observable은 변환 전 소스 Observable들이 배출한 항목들을 배출한다 Zip — 명시한 함수를 통해 여러 Observable들이 배출한 항목들을 결합하고 함수의 실행 결과를 배출한

오류 처리 연산자, Observable 유틸리티 연산자, 조건과 불린 연산자(Boolean), 수학과 집계 연산자, 역압(Backpressure) 연산자, ...

http://reactivex.io/documentation/ko/operators.html

Create Observable

Transform

Subscribe

onNext(T t)

onError(Throwable e)

onCompleted()

onSubscribe

진짜로 시작하기

이미지 https://i.ytimg.com/vi/oLy8hMW-e48/maxresdefault.jpg

경고

지금부터 나오는 소스코드는 완벽한 것이 아니므로, 부족한 부분은 스스로 잘 고쳐서 안정적인 프로그래밍을 부탁드리며,

아울러 발표자에게도 한 수 가르쳐주시길😀

Icon Author: http://www.flaticon.com/authors/gregor-cresnar

시나리오1. 키워드를 넣으면 서버 API를 통해 연관 검색어를 동적으로 보여주기

TextView에 OnTextChangedListener를 달고,

입력된 텍스트로 HTTP 클라이언트를 통해 Suggestion 텍스트 리스트를 받아온다.

받아온 리스트를 밑에 있는 RecyclerView의 Adapter에 반영한다.

@OnTextChanged(R.id.keyword) public void keywordChanged() { }

RxTextView.textChanges(R.id.keyword);

without Rx with Rx

TextView에 OnTextChangedListener를 달고,

without Rx with Rx

TextView에 OnTextChangedListener를 달고, 입력된 텍스트로 HTTP 클라이언트를 통해 Suggestion 텍스트 리스트를 받아온다.

public interface MyRestService { @GET("suggest/{keyword}") Call<List<String>> listSuggestions( @Path("keyword") String keyword); }

@OnTextChanged(R.id.keyword) public void keywordChanged(String keyword) { Call<List<String>> suggestions = service.listSuggestions(keyword); }

public interface MyRestService { @GET("suggest/{keyword}") Observable<List<String>> listSuggestions( @Path("keyword") String keyword); }

RxTextView.textChanges(R.id.keyword) .map(CharSequence::toString) .flatMap(text -> service.listSuggestions(text));

without Rx with Rx

TextView에 OnTextChangedListener를 달고, 입력된 텍스트로 HTTP 클라이언트를 통해 Suggestion 텍스트 리스트를 받아온다.

받아온 리스트를 밑에 있는 RecyclerView의 Adapter에 반영한다.

public interface MyRestService { @GET("suggest/{keyword}") Call<List<String>> listSuggestions( @Path("keyword") String keyword); }

@OnTextChanged(R.id.keyword) public void keywordChanged(String keyword) { Call<List<String>> suggestions = service.listSuggestions(keyword); suggestions.enqueue(new Callback<List<String>>() { @Override public void onResponse(Call<List<String>> call, Response<List<String>> response) { List<String> texts = response.body(); for (String t : texts) { adapter.add(t); } }

@Override public void onFailure(Call<List<String>> call, Throwable t) {

} }); }

public interface MyRestService { @GET("suggest/{keyword}") Observable<List<String>> listSuggestions( @Path("keyword") String keyword); }

RxTextView.textChanges(R.id.keyword) .map(CharSequence::toString) .flatMap(text -> service.listSuggestions(text)) .flatMapIterable(texts -> texts) .subscribe(adapter::add);

뭔가 어색해 조금 더 사용자에게 친화적이면 좋겠어

버전 올라가는 소리가 들려

키워드 입력하는 시점에 왜 이전 키워드에 대한 리스트를 잠깐 보여주지?이게 되는 건가? 안 되는 건가?

에러 나는데 제가 뭘 잘못한건가요?

약간 좀 느린 느낌인데 더 빠르게 안되나요?

TextView에 OnTextChangedListener를 달고,

입력된 텍스트로 HTTP 클라이언트를 통해 Suggestion 텍스트 리스트를 받아온다.

받아온 리스트를 밑에 있는 RecyclerView의 Adapter에 반영한다.

RecyclerView의 Adapter의 데이터를 비워준다.

끝나면 ProgressBar를 숨긴다.

ProgressBar를 보여준다.

하다가 뭔가 문제가 생기면 사용자한테 친절하게 알려준다. SnackBar를 보여주자.

TextView에 텍스트가 없거나 스페이스만 있을 때는 네트워크까지 가지 말자.

without Rx with Rx

TextView에 OnTextChangedListener를 달고, 시작하기 전에 Adapter에 데이터를 지운다.

입력된 텍스트로 HTTP 클라이언트를 통해 Suggestion 텍스트 리스트를 받아온다. 받아온 리스트를 밑에 있는 RecyclerView의 Adapter에 반영한다.

@OnTextChanged(R.id.keyword) public void keywordChanged(String keyword) { adapter.clear(); Call<List<String>> suggestions = service.listSuggestions(keyword); suggestions.enqueue(new Callback<List<String>>() { @Override public void onResponse(Call<List<String>> call, Response<List<String>> response) { List<String> texts = response.body(); for (String t : texts) { adapter.add(t); } }

@Override public void onFailure(Call<List<String>> call, Throwable t) {

} }); }

RxTextView.textChanges(R.id.keyword) .map(CharSequence::toString) .flatMap(text -> service.listSuggestions(text)) .flatMapIterable(texts -> texts) .subscribe(adapter::add, e -> {}, () -> {}, __ -> adapter.clear());

without Rx with Rx

TextView에 OnTextChangedListener를 달고, 시작하기 전에 Adapter에 데이터를 지우고 프로그레스바를 보여준다.

입력된 텍스트로 HTTP 클라이언트를 통해 Suggestion 텍스트 리스트를 받아온다. 받아온 리스트를 밑에 있는 RecyclerView의 Adapter에 반영한다.

끝나면 프로그레스바를 숨긴다.

@OnTextChanged(R.id.keyword) public void keywordChanged(String keyword) { adapter.clear(); progressBar.setVisibility(VISIBLE); Call<List<String>> suggestions = service.listSuggestions(keyword); suggestions.enqueue(new Callback<List<String>>() { @Override public void onResponse(Call<List<String>> call, Response<List<String>> response) { List<String> texts = response.body(); for (String t : texts) { adapter.add(t); } progressBar.setVisibility(GONE); }

@Override public void onFailure(Call<List<String>> call, Throwable t) { } }); }

RxTextView.textChanges(R.id.keyword) .map(CharSequence::toString) .flatMap(text -> service.listSuggestions(text)) .flatMapIterable(texts -> texts) .subscribe(adapter::add, e -> {}, () -> progressBar.setVisibility(GONE), __ -> { adapter.clear(); progressBar.setVisibility(VISIBLE); });

without Rx with Rx

TextView에 OnTextChangedListener를 달고, 시작하기 전에 Adapter에 데이터를 지우고 프로그레스바를 보여준다.

입력된 텍스트로 HTTP 클라이언트를 통해 Suggestion 텍스트 리스트를 받아온다. 받아온 리스트를 밑에 있는 RecyclerView의 Adapter에 반영한다.

끝나면 프로그레스바를 숨긴다. 에러가 발생하면 스낵바를 보여준다.

@OnTextChanged(R.id.keyword) public void keywordChanged(String keyword) { adapter.clear(); progressBar.setVisibility(VISIBLE); Call<List<String>> suggestions = service.listSuggestions(keyword); suggestions.enqueue(new Callback<List<String>>() { @Override public void onResponse(Call<List<String>> call, Response<List<String>> response) { List<String> texts = response.body(); for (String t : texts) { adapter.add(t); } progressBar.setVisibility(GONE); }

@Override public void onFailure(Call<List<String>> call, Throwable t) { SnackBar.make(view, "Something wrong!", LENGTH_LONG).show(); Log.d(TAG, "Error on keywordChanged.", t); } }); }

RxTextView.textChanges(R.id.keyword) .map(CharSequence::toString) .flatMap(text -> service.listSuggestions(text)) .flatMapIterable(texts -> texts) .subscribe(adapter::add, e -> { SnackBar.make(view, "Something wrong!", LENGTH_LONG).show(); Log.d(TAG, "Error on keywordChanged.", e); }, () -> progressBar.setVisibility(GONE), __ -> { adapter.clear(); progressBar.setVisibility(VISIBLE); });

without Rx with Rx

TextView에 OnTextChangedListener를 달고, 입력된 키워드가 유효한지 체크한다.

시작하기 전에 Adapter에 데이터를 지우고 프로그레스바를 보여준다. 입력된 텍스트로 HTTP 클라이언트를 통해 Suggestion 텍스트 리스트를 받아온다.

받아온 리스트를 밑에 있는 RecyclerView의 Adapter에 반영한다. 끝나면 프로그레스바를 숨긴다. 에러가 발생하면 스낵바를 보여준다.

@OnTextChanged(R.id.keyword) public void keywordChanged(String keyword) { if(““.equals(keyword.trim()) return; adapter.clear(); progressBar.setVisibility(VISIBLE); Call<List<String>> suggestions = service.listSuggestions(keyword); suggestions.enqueue(new Callback<List<String>>() { @Override public void onResponse(Call<List<String>> call, Response<List<String>> response) { List<String> texts = response.body(); for (String t : texts) { adapter.add(t); } progressBar.setVisibility(GONE); }

@Override public void onFailure(Call<List<String>> call, Throwable t) { SnackBar.make(view, "Something wrong!", LENGTH_LONG).show(); Log.d(TAG, "Error on keywordChanged.", t); } }); }

RxTextView.textChanges(R.id.keyword) .map(CharSequence::toString) .map(String::trim) .filter(t -> !"".equals(t)) .flatMap(text -> service.listSuggestions(text)) .flatMapIterable(texts -> texts) .subscribe(adapter::add, e -> { SnackBar.make(view, "Something wrong!", LENGTH_LONG).show(); Log.d(TAG, "Error on keywordChanged.", e); }, () -> progressBar.setVisibility(GONE), __ -> { adapter.clear(); progressBar.setVisibility(VISIBLE); });

RxJava로 짠 코드 테스트 할 수 있나요? (yn)

yes, but think of the thread

Congratulations! Level 2

• Hot/Cold Observable

• more APIs of RxJava

• Test for each onNext

Next Quests

http://wowguidessource.com/5-wow-leveling-tips-to-acquire-world-of-warcraft-level-85-fast.php

• Future<T>, Single<T>

Reference• ReactiveX 공식 사이트 http://reactivex.io/

• GDG 2014 - RxJava를 활용한 Functional Reactive Programming https://www.slideshare.net/waynejo/gdg-41029986

• 웹 프론트엔드 개발자의 얕고 넓은 Rx 이야기 https://www.slideshare.net/jeokrang/rx-70197043

• 동기와 비동기, 블로킹과 논블로킹 http://nsinc.tistory.com/108

• http://nsinc.tistory.com/108

thxEOP