이는 Android에서 RxJava를 사용하는 실제 유용한 예제가 포함된 저장소입니다. 일반적으로 "진행 중"(WIP) 상태가 지속됩니다.
나는 또한 이 저장소에 나열된 많은 예를 사용하여 Learning Rx에 대해 이야기해 왔습니다.
using
)일반적인 요구 사항은 길고 과중한 I/O 집약적 작업을 백그라운드 스레드(비 UI 스레드)로 오프로드하고 완료 시 결과를 UI/기본 스레드로 다시 공급하는 것입니다. 이는 장기 실행 작업을 백그라운드 스레드로 오프로드하는 방법에 대한 데모입니다. 작업이 완료되면 메인 스레드에서 다시 재개됩니다. 모두 RxJava를 사용합니다! 이것을 AsyncTasks의 대체품으로 생각하십시오.
긴 작업은 Thread.sleep 호출을 차단하여 시뮬레이션됩니다(이 작업은 백그라운드 스레드에서 수행되므로 UI가 중단되지 않습니다).
이 예가 빛나는 것을 실제로 보려면. 버튼을 여러 번 누르고 긴 작업이 백그라운드에서만 실행되기 때문에 버튼 클릭(UI 작업)이 어떻게 차단되지 않는지 확인하세요.
이것은 "버퍼" 작업을 사용하여 이벤트를 누적하는 방법에 대한 데모입니다.
버튼이 제공되고 우리는 일정 기간 동안 해당 버튼에 대한 클릭 수를 누적한 다음 최종 결과를 내보냅니다.
버튼을 한 번 누르면 버튼을 한 번 눌렀다는 메시지가 표시됩니다. 2초 내에 연속해서 5번 누르면 해당 버튼을 5번 눌렀다는 단일 로그가 생성됩니다("버튼이 한 번 눌림"이라는 개별 로그 5개와 비교).
메모:
특정 시간 범위 내의 탭 수보다 "연속" 탭을 누적하는 보다 완벽한 솔루션을 찾고 있다면 publish
및 buffer
연산자의 조합이 사용되는 EventBus 데모를 살펴보세요. 더 자세한 설명을 보려면 이 블로그 게시물을 살펴보세요.
이것은 마지막 이벤트만 존중하는 방식으로 이벤트를 삼킬 수 있는 방법에 대한 데모입니다. 이에 대한 일반적인 예는 즉시 검색 결과 상자입니다. "Bruce Lee"라는 단어를 입력할 때 B, Br, Bru, Bruce, Bruce, Bruce L ... 등에 대한 검색을 실행하고 싶지는 않습니다. 대신 몇 분 동안 지능적으로 기다리십시오. 단어 전체를 입력한 다음 "Bruce Lee"라는 전화를 한 번 울립니다.
입력 상자에 입력할 때 입력 문자가 바뀔 때마다 로그 메시지를 내보내지 않고 마지막으로 발생한 이벤트(예: 입력)만 선택하여 기록합니다.
이것은 RxJava의 debounce/throttleWithTimeout 메소드입니다.
Square의 Retrofit은 쉬운 네트워킹에 도움이 되는 놀라운 라이브러리입니다(아직 RxJava로 전환하지 않았더라도 꼭 확인해 보세요). RxJava와 함께 사용하면 훨씬 더 잘 작동하며 GitHub API에 대한 예제는 Netflix에서 안드로이드 반신 개발자인 Jake Wharton의 강연에서 직접 가져온 것입니다. 이 링크에서 강연을 시청하실 수 있습니다. 덧붙여서 RxJava를 사용하게 된 동기는 Netflix에서 열린 이 강연에 참석한 것이었습니다.
(참고: GitHub API 할당량에 매우 빠르게 도달할 가능성이 높으므로 이러한 예제를 자주 실행하려면 OAuth 토큰을 매개변수로 보내십시오.)
자동 업데이트 뷰는 매우 멋진 기능입니다. 이전에 Angular JS를 다루었다면 "양방향 데이터 바인딩"이라는 매우 멋진 개념이 있으므로 HTML 요소가 모델/엔티티 객체에 바인딩되면 해당 엔터티의 변경 사항을 지속적으로 "수신"하고 모델을 기반으로 상태를 자동 업데이트합니다. 이 예제의 기술을 사용하면 프레젠테이션 뷰 모델 패턴과 같은 패턴을 매우 쉽게 사용할 수 있습니다.
여기의 예는 매우 기초적이지만 Publish Subject
사용하여 이중 바인딩을 달성하는 데 사용되는 기술은 훨씬 더 흥미롭습니다.
이것은 RxJava 스케줄러를 사용한 폴링의 예입니다. 이는 지속적으로 서버를 폴링하고 새 데이터를 얻으려는 경우에 유용합니다. 네트워크 호출은 "시뮬레이트"되므로 결과 문자열을 반환하기 전에 지연이 발생합니다.
이에 대한 두 가지 변형이 있습니다.
두 번째 예는 기본적으로 지수 백오프의 변형입니다.
여기서는 RetryWithDelay를 사용하는 대신 RepeatWithDelay를 사용합니다. Retry(When)와 Repeat(When)의 차이점을 이해하려면 해당 주제에 대한 Dan의 환상적인 게시물을 제안하겠습니다.
repeatWhen
을 사용하지 않고 지연된 폴링에 대한 대체 접근 방식은 체인으로 중첩된 지연 관찰 가능 항목을 사용하는 것입니다. ExponentialBackOffFragment 예제에서 startExecutingWithExponentialBackoffDelay를 참조하세요.
지수 백오프는 특정 출력의 피드백을 기반으로 프로세스 속도를 변경하는 전략입니다(일반적으로 재시도 횟수를 줄이거나 특정 프로세스를 재시도 또는 재실행하기 전 대기 시간을 늘림).
이 개념은 예제를 통해 더 이해가 됩니다. RxJava를 사용하면 이러한 전략을 구현하는 것이 (상대적으로) 간단합니다. 아이디어를 제안해준 Mike에게 감사드립니다.
네트워크 장애가 발생했다고 가정해 보겠습니다. 합리적인 전략은 1초마다 네트워크 호출을 계속 재시도하지 않는 것입니다. 대신 지연 시간을 늘려 재시도하는 것이 현명합니다(아니... 우아합니다!). 그러면 두 번째 1에서 네트워크 호출을 실행하려고 하는데 주사위가 없습니까? 10초 후에 시도해 보세요... 부정인가요? 20초 후에 시도해 보세요. 쿠키가 없나요? 1분 후에 시도해 보세요. 그래도 실패하면 네트워크를 포기해야 해요 yo!
retryWhen
연산자와 함께 RxJava를 사용하여 이 동작을 시뮬레이션합니다.
RetryWithDelay
코드 조각 제공:
또한 매우 유사한 지수 백오프 메커니즘을 사용하는 폴링 예제를 살펴보세요.
지수 백오프 전략의 또 다른 변형은 지정된 횟수만큼 지연된 간격으로 작업을 실행하는 것입니다. 즉, 지금부터 1초 후에 특정 작업을 실행하고, 지금부터 10초 후에 다시 실행하고, 그 다음 20초 후에 해당 작업을 실행하는 것입니다. 총 3회 후에는 실행이 중지됩니다.
이 동작을 시뮬레이션하는 것은 실제로 이전 재시도 메커니즘보다 훨씬 더 간단합니다. 이를 달성하기 위해 delay
연산자의 변형을 사용할 수 있습니다.
.combineLatest
사용)단편화된 팟캐스트 에피소드 #4(4시 30분쯤)에서 이 아이디어를 제공한 Dan Lew에게 감사드립니다.
.combineLatest
사용하면 단일 위치에서 동시에 여러 관찰 가능 항목의 상태를 컴팩트하게 모니터링할 수 있습니다. 설명된 예에서는 .combineLatest
사용하여 기본 양식의 유효성을 검사하는 방법을 보여줍니다. 이 양식에는 "유효한" 것으로 간주되는 3가지 기본 입력(이메일, 비밀번호 및 숫자)이 있습니다. 모든 입력이 유효하면 양식이 유효해집니다(아래 텍스트가 파란색으로 변합니다:P). 그렇지 않은 경우 잘못된 입력에 대해 오류가 표시됩니다.
각 양식 필드의 텍스트/입력 변경 사항을 추적하는 3개의 독립적인 관찰 가능 항목이 있습니다(RxAndroid의 WidgetObservable
텍스트 변경 사항을 모니터링하는 데 유용합니다). 3개의 입력 모두 에서 이벤트 변경이 감지되면 결과가 "결합"되고 양식의 유효성이 평가됩니다.
유효성을 확인하는 Func3
함수는 3개의 입력이 모두 텍스트 변경 이벤트를 받은 후에만 시작됩니다.
이 기술의 가치는 양식에 입력 필드 수가 많을수록 더욱 분명해집니다. 여러 부울 값을 사용하여 다르게 처리하면 코드가 복잡해지고 따라가기가 어려워집니다. 그러나 .combineLatest
사용하면 모든 논리가 멋지고 컴팩트한 코드 블록에 집중됩니다(저는 여전히 부울을 사용하지만 이는 예제를 더 읽기 쉽게 만들기 위한 것입니다).
디스크(빠른) 캐시와 네트워크(신선) 호출이라는 두 가지 소스 Observable이 있습니다. 일반적으로 디스크 Observable은 네트워크 Observable보다 훨씬 빠릅니다. 그러나 작업을 시연하기 위해 운영자가 어떻게 작동하는지 확인하기 위해 가짜 "느린" 디스크 캐시도 사용했습니다.
이는 4가지 기술을 사용하여 시연됩니다.
.concat
.concatEager
.merge
.publish
선택기 + 병합 + takeUntil네 번째 기술은 아마도 결국 사용하고 싶은 것이지만 기술의 진행 과정을 살펴보고 그 이유를 이해하는 것은 흥미로울 것입니다.
concat
훌륭합니다. 이는 첫 번째 Observable(우리의 경우 디스크 캐시)에서 정보를 검색한 다음 후속 네트워크 Observable에서 정보를 검색합니다. 디스크 캐시가 아마도 더 빠르기 때문에 모든 것이 잘 나타나고 디스크 캐시가 빠르게 로드되며, 네트워크 호출이 끝나면 "새로운" 결과를 교체합니다.
concat
의 문제점은 첫 번째 Observable이 완료될 때까지 후속 Observable이 시작되지 않는다는 것입니다. 문제가 될 수 있습니다. 우리는 모든 관찰 가능 항목이 동시에 시작되기를 원하지만 예상한 방식으로 결과를 생성합니다. 고맙게도 RxJava는 이를 정확하게 수행하는 concatEager
도입했습니다. 두 Observable을 모두 시작하지만 이전 Observable이 완료될 때까지 후자의 결과를 버퍼링합니다. 이것은 완전히 실행 가능한 옵션입니다.
하지만 때로는 결과를 즉시 표시하기 시작하고 싶을 때도 있습니다. 이상한 이유로 첫 번째 Observable이 모든 항목을 실행하는 데 정말 오랜 시간이 걸린다고 가정하면, 두 번째 Observable의 처음 몇 항목이 전송되더라도 강제로 대기열에 추가됩니다. Observable에서 반드시 "대기"하고 싶지는 않습니다. 이러한 상황에서는 merge
연산자를 사용할 수 있습니다. 항목이 방출될 때 항목을 인터리브합니다. 이것은 훌륭하게 작동하며 결과가 표시되자마자 결과를 내뱉기 시작합니다.
concat
연산자와 유사하게 첫 번째 Observable이 두 번째 Observable보다 항상 빠르면 문제가 발생하지 않습니다. 그러나 merge
의 문제는 다음과 같습니다. 이상한 이유로 항목이 캐시에 의해 방출되거나 최신/최신 Observable 이후 느린 Observable에 의해 방출되면 최신 콘텐츠를 덮어씁니다. 이 문제가 실제로 발생하는 것을 보려면 예제에서 "MERGE (SLOWER DISK)" 버튼을 클릭하십시오. @JakeWharton 및 @swankjesse 기여도가 0이 됩니다! 실제 세계에서는 이는 좋지 않을 수 있습니다. 이는 최신 데이터가 오래된 디스크 데이터에 의해 무시된다는 의미이기 때문입니다.
이 문제를 해결하려면 "선택기"를 사용하는 매우 멋진 publish
연산자와 함께 병합을 사용할 수 있습니다. 나는 이 사용법에 대해 블로그 게시물에 썼지만 이 기술을 상기시켜 준 Jedi JW에게 감사드립니다. 우리는 네트워크 관찰 가능 항목 publish
네트워크 관찰 가능 항목이 방출되기 시작할 때까지 디스크 캐시에서 방출을 시작하는 선택기를 제공합니다. 네트워크 관찰 가능 항목이 방출되기 시작하면 디스크 관찰 가능 항목의 모든 결과를 무시합니다. 이것은 완벽하며 우리가 겪을 수 있는 모든 문제를 처리합니다.
이전에는 merge
연산자를 사용했지만 "resultAge"를 모니터링하여 결과를 덮어쓰는 문제를 극복했습니다. 이 이전 구현을 보고 싶다면 이전 PseudoCacheMergeFragment
예제를 참조하세요.
이것은 RxJava의 timer
, interval
및 delay
연산자를 사용하여 특정 간격으로 작업을 실행하려는 여러 경우를 처리하는 방법을 보여주는 매우 간단하고 간단한 예입니다. 기본적으로 Android TimerTask
에 NO라고 말하세요.
여기에 설명된 사례:
이 데모의 세부 사항을 훨씬 더 잘 설명하는 블로그 게시물이 함께 제공됩니다.
Android에서 RxJava를 사용할 때 묻는 일반적인 질문은 "구성 변경(활동 회전, 언어 로케일 변경 등)이 발생하는 경우 관찰 가능 항목의 작업을 어떻게 재개합니까?"입니다.
이 예는 하나의 전략 즉, 하나를 보여줍니다. 보유된 조각을 사용합니다. 나는 꽤 오래전에 Alex Lockwood가 작성한 이 환상적인 게시물을 읽은 후 보유된 조각을 "작업자 조각"으로 사용하기 시작했습니다.
시작 버튼을 누르고 화면을 마음껏 회전하세요. 관찰 가능한 부분이 중단된 부분부터 계속되는 것을 볼 수 있습니다.
이 예에서 사용된 관찰 가능한 소스의 "핫함"에 대한 특정 단점이 있습니다. 자세한 내용을 설명하는 내 블로그 게시물을 확인하세요.
이후 나는 다른 접근 방식을 사용하여 이 예제를 다시 작성했습니다. ConnectedObservable
접근 방식이 작동하는 동안 까다로울 수 있는 "멀티캐스팅" 영역에 들어갑니다(스레드 안전성, .refcount 등). 반면에 주제는 훨씬 더 간단합니다. 여기에서 Subject
사용하여 다시 작성된 것을 볼 수 있습니다.
나는 몇 가지 구체적인 내용을 다루는 주제에 대해 생각하는 방법에 대한 또 다른 블로그 게시물을 썼습니다.
Volley는 Google이 IO '13에서 소개한 또 다른 네트워킹 라이브러리입니다. github의 친절한 시민이 이 예제를 제공하여 Volley를 RxJava와 통합하는 방법을 알 수 있습니다.
여기서는 주제의 간단한 사용을 활용합니다. 솔직히 말해서 이미 Observable
을 통해 항목이 내려오고 있지 않다면(예를 들어 Retrofit이나 네트워크 요청을 통해) Rx를 사용하여 일을 복잡하게 만들 이유가 없습니다.
이 예에서는 기본적으로 페이지 번호를 제목으로 보내고 제목은 항목 추가를 처리합니다. concatMap
의 사용과 _itemsFromNetworkCall
에서 Observable<List>
의 반환을 확인하세요.
재미삼아 PaginationAutoFragment
예제도 포함했습니다. 이 예제는 버튼을 누를 필요 없이 "자동으로 페이지를 매깁니다". 이전 예제의 작동 방식을 이해했다면 따라하는 것이 간단할 것입니다.
다음은 몇 가지 다른 멋진 구현입니다(읽는 것은 즐거웠지만 개인적으로는 필요하지 않다고 생각하므로 실제 앱에 사용하지는 않았습니다).
아래 ASCII 다이어그램은 위풍당당하게 다음 예제의 의도를 표현합니다. f1,f2,f3,f4,f5는 본질적으로 네트워크 호출로, 수행 시 향후 계산에 필요한 결과를 반환합니다.
(flatmap)
f1 ___________________ f3 _______
(flatmap) | (zip)
f2 ___________________ f4 _______| ___________ final output
|
____________ f5 _______|
이 예제의 코드는 이미 interwebs의 Mr.skehlet에 의해 작성되었습니다. 코드의 요점으로 이동하세요. 이는 순수 Java(6)로 작성되었으므로 이전 예제를 이해했다면 꽤 이해하기 쉽습니다. 시간이 허락하거나 다른 설득력 있는 예가 부족하면 여기서 다시 정리하겠습니다.
이것은 .timeout
연산자의 사용을 보여주는 간단한 예입니다. 버튼 1은 시간 초과 제한 이전에 작업을 완료하고, 버튼 2는 시간 초과 오류를 강제합니다.
타임아웃 예외 상황에서 어떻게 반응하는지를 나타내는 커스텀 Observable을 어떻게 제공할 수 있는지 주목하세요.
using
) using
운영자는 상대적으로 덜 알려져 있으며 Google에 어려운 것으로 악명이 높습니다. (비용이 많이 드는) 리소스를 설정하고 사용한 후 깔끔한 방식으로 폐기하는 데 도움이 되는 아름다운 API입니다.
이 연산자의 좋은 점은 제한된 범위에서 잠재적으로 비용이 많이 드는 리소스를 사용할 수 있는 메커니즘을 제공한다는 것입니다. -> 설정을 사용하여 사용하고 폐기합니다. Realm 인스턴스와 같은 DB 연결, 소켓 연결, 스레드 잠금 등을 생각해 보세요.
Rx의 멀티캐스팅은 어둠의 예술과 같습니다. 걱정 없이 해낼 수 있는 방법을 아는 사람은 그리 많지 않습니다. 이 예에서는 두 명의 가입자(버튼 형태)를 조정하고 서로 다른 시점에 가입자를 추가/제거하고 이러한 상황에서 다양한 운영자가 어떻게 행동하는지 확인할 수 있습니다.
소스 Observale은 타이머( interval
) Observable이며 이를 선택한 이유는 종료되지 않는 Observable을 의도적으로 선택하기 위한 것이었습니다. 따라서 멀티캐스트 실험이 누출되는지 테스트/확인할 수 있습니다.
360|Andev에서 멀티캐스팅에 대해 자세히 강의하기도 했습니다. 의향과 시간이 있다면 해당 강연(특히 멀티캐스트 연산자 순열 부분)을 먼저 시청한 다음 여기에 있는 예를 살펴보는 것이 좋습니다.
여기에 있는 모든 예제는 RxJava 2.X를 사용하도록 마이그레이션되었습니다.
RxBindings, RxRelays, RxJava-Math 등과 같은 특정 라이브러리가 아직 2.x로 포팅되지 않았기 때문에 경우에 따라 David Karnok의 Interop 라이브러리를 사용합니다.
나는 예제가 지나치게 인위적이지 않고 실제 사용 사례를 반영하도록 노력합니다. RxJava 사용을 보여주는 유사한 유용한 예제가 있다면 언제든지 끌어오기 요청을 보내주세요.
나도 RxJava에 대해 고민 중이므로 위에서 언급한 예제 중 하나를 수행하는 더 좋은 방법이 있다고 생각되면 방법을 설명하는 문제를 열어보세요. 더 나은 방법은 끌어오기 요청을 보내는 것입니다.
Rx 스레딩은 지저분한 작업입니다. 도움을 주기 위해 이 프로젝트에서는 분석을 위해 YourKit 도구를 사용합니다.
YourKit은 Java 애플리케이션을 모니터링하고 프로파일링하기 위한 혁신적이고 지능적인 도구를 사용하여 오픈 소스 프로젝트를 지원합니다. YourKit은 YourKit Java Profiler의 작성자입니다.
Apache 라이센스 버전 2.0("라이센스")에 따라 라이센스가 부여됩니다. 다음에서 라이센스 사본을 얻을 수 있습니다.
http://www.apache.org/licenses/LICENSE-2.0
해당 법률에서 요구하거나 서면으로 동의하지 않는 한, 라이선스에 따라 배포되는 소프트웨어는 명시적이든 묵시적이든 어떠한 종류의 보증이나 조건 없이 "있는 그대로" 배포됩니다. 라이선스에 따른 허가 및 제한 사항을 관리하는 특정 언어는 라이선스를 참조하세요.
귀하는 수정 사항, 풀 요청, 새 예제 등의 형태로 이 저장소에 대한 모든 기여가 위에 언급된 라이센스를 따른다는 데 동의합니다.