Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Androidアプリ開発からみた RxJavaの使いどころ

Androidアプリ開発からみた RxJavaの使いどころ

JJUG CCC 2017 Spring #ccc_i3

Naoki Morioka

May 16, 2017
Tweet

More Decks by Naoki Morioka

Other Decks in Technology

Transcript

  1. ϦΞΫςΟϒϓϩάϥϛϯάᶅ w σʔλετϦʔϜͷੜ੒΍ɺม׵ɺ߹੒Λߦ͏ w ετϦʔϜ͔ΒདྷΔσʔλΛड͚औͬͨॲཧ ߪಡ Λهࡌ͢Δ " # $

    Πϕϯτ " " Πϕϯτ ʜ B C D B B 4USJOH ͂ TUP-PXFS$BTF ม׵ 4ZTUFNPVUQSJOUMO ߪಡ B C D ʜ
  2. 3FBDUJWF4USFBNTΠϯλʔϑΣʔε Publisher public void subscribe(Subscriber<? super T> s); Subscriber public

    void onSubscribe(Subscription s); public void onNext(T t); public void onError(Throwable t); public void onComplete(); /EBUBSFRVFTU /EBUBTUSFBNJOH TVCTDSJCF Subscription public void request(long n); public void cancel(); 1VCMJTIFS͕σʔλΛྲྀ͠4VCTDSJCFS͕σʔλΛड͚औΔॲཧ͕جຊ Processor public interface Processor<T, R> extends Subscriber<T>, Publisher<R> { }
  3. ߪಡॲཧ Flowable.just("1", "2", "3", "4") .subscribe(new Subscriber<String>() { public void

    onSubscribe(Subscription subscription) { subscription.request(Long.MAX_VALUE); System.out.println("subscribed"); } public void onNext(String s) { int num = Integer.parseInt(s); System.out.println(num); } public void onError(Throwable throwable) { System.out.println("error"); } public void onComplete() { System.out.println("completed"); } }); TVCTDSJCFE     DPNQMFUFE SFTVMU ੜ੒ ᶃ։࢝ ᶄσʔλऔಘ ᶅਖ਼ৗऴྃ
  4. ߪಡॲཧ ؔ਺ܕ*OUFSGBDF +BWB Flowable.just("1", "2", "3", "4")
 .subscribe(s -> {


    int num = Integer.parseInt(s);
 System.out.println(num);
 }, throwable -> {
 System.out.println("error : " + throwable.getMessage());
 });     SFTVMU /FYUॲཧϋϯυϦϯά Τϥʔॲཧ
  5. ߪಡॲཧ ؔ਺ܕ*OUFSGBDF +BWB public final Disposable subscribe(Consumer<? super T> onNext,

    Consumer<? super Throwable> onError, Action onComplete, Consumer<? super Subscription> onSubscribe) { LambdaSubscriber<T> ls = new LambdaSubscriber( onNext, onError, onComplete, onSubscribe); this.subscribe((FlowableSubscriber)ls); return ls; } ಺෦తʹ4VCTDSJCFSΛ࡞੒ͯ͠ॲཧ͍ͯ͠Δ
  6. 4USFBN"1* ࠶ར༻ෆՄ Stream<String> st = Arrays.asList("1", "2", "3", "4").stream();
 st.forEach(s

    -> {
 System.out.println(s);
 }); System.out.println(“next"); 
 st.forEach(s -> {
 System.out.println(s);
 });     OFYU KBWBMBOH*MMFHBM4UBUF&YDFQUJPOTUSFBNIBTBMSFBEZCFFOPQFSBUFEVQPOPSDMPTFE  BUKBWBVUJMTUSFBN"CTUSBDU1JQFMJOFJOJU "CTUSBDU1JQFMJOFKBWB  ʜ SFTVMU
  7. 'MPXBCMF ࠶ར༻Մೳ Flowable<String> flowable = Flowable.just("1", "2", "3", "4"); flowable.subscribe(s

    -> { System.out.println(s); }); System.out.println("next"); flowable.subscribe(s -> { System.out.println(s); });     OFYU     SFTVMU
  8. 'MPXBCMF ྫ֎ͷѻ͍ Flowable.just("1", "2", "three", “4") .map(Integer::parseInt)
 .subscribe(num -> {


    System.out.println(num);
 }
 , throwable -> {
 System.out.println("error : " + throwable.getMessage());
 });   FSSPS'PSJOQVUTUSJOHUISFF SFTVMU /VNCFS'PSNBU&YDFQUJPO Τϥʔऴྃ
  9. 'MPXBCMF ࣮ߦ5ISFBE੍ޚ Flowable.just("a", "b", "c", "d") .map(s -> { System.out.println(Thread.currentThread().getName()

    + " : " + s); return s.toUpperCase(); }) .subscribeOn(Schedulers.newThread()) .observeOn(Schedulers.computation()) .blockingSubscribe(s -> { System.out.println(Thread.currentThread().getName() + " : " + s); }); 3Y/FX5ISFBE4DIFEVMFSB 3Y/FX5ISFBE4DIFEVMFSC 3Y/FX5ISFBE4DIFEVMFSD 3Y/FX5ISFBE4DIFEVMFSE 3Y$PNQVUBUJPO5ISFBE1PPM" 3Y$PNQVUBUJPO5ISFBE1PPM# 3Y$PNQVUBUJPO5ISFBE1PPM$ 3Y$PNQVUBUJPO5ISFBE1PPM% SFTVMU TVCTDSJCF0O'MPXBCMF0CTFSWFCMFॲཧͷ࣮ߦεϨου PCTFSWF0OͦΕҎ߱ͷ࣮ߦεϨου ݩͷ'MPXBCMFͷ࣮ߦεϨουΛࢦఆ ͜͜Ҏ߱ͷ࣮ߦεϨουΛࢦఆ
  10. ม׵ॲཧ NBQ Flowable.just(1, 2, 3, 4) .map(num -> num *

    num)
 .subscribe(num -> {
 System.out.println(num);
 });     SFTVMU NBQ͸ετϦʔϜ಺ͷσʔλͦͷ΋ͷΛม׵͢Δ
  11. ม׵ॲཧ qBU.BQ Flowable.just(1, 2) .flatMap(v -> { // new flowable

    return Flowable.just(v, 2 * v); }) .subscribe(System.out::println);     SFTVMU qBU.BQ͸σʔλ͔Β'MPXBCMFΛ࡞੒ͯ͠ ࠷ऴతʹl'MPXBCMFΛऔΓ෷ͬͯzετϦʔϜΛߏங͢Δ " 'MPXBCMF " " # $ 'MPXBCMF # # 'MPXBCMF $ $ " # $ " # $
  12. ม׵ॲཧ qBU.BQ public Flowable<Integer> square(int v) { return Flowable.just(v).map(w ->

    w * w); } public Flowable<Integer> twin(int v) { return Flowable.just(v, v); }; Flowable<Integer> f1 = Flowable.just(1,2); Flowable<Integer> f2 = f1.flatMap(v -> square(v)); Flowable<Integer> f3 = f2.flatMap(v -> twin(v)); f1.subscribe(v1 -> System.out.println("f1 : " + v1)); f2.subscribe(v1 -> System.out.println("f2 : " + v1)); f3.subscribe(v1 -> System.out.println("f3 : " + v1)); G G G G G G G G SFTVMU 'MPXBCMF͸ʮ͋Δঢ়ଶʯͷσʔλετ ϦʔϜΛఆٛ͠ ม਺ͷΑ͏ʹѻ͑Δ
  13. w ετϦʔϜͷੜ੒ॲཧ KVTUGSPN$BMMBCMFGSPN"SSBZͳͲ w σʔλม׵ॲཧ pMUFSNBQqBU.BQͳͲ w ߹੒ॲཧ NFSHF[JQDPNCJOF-BUFTUͳͲ w

    ෭࡞༻ ߪಡऀ΁ͷ௨஌ͱ͸ผͷॲཧ  EP0O4VCTDSJCFEP0O/FYUEP0O$PNQMFUFͳͲ ΦϖϨʔλͷछผ ม׵ ੜ੒ TVCTDSJCF ߹੒ ෭࡞༻ ੜ੒ ม׵
  14. )PUͳੜ࢈ऀ 1SPDFTTPS PublishProcessor<Integer> processor = PublishProcessor.create(); processor.subscribe(num -> { System.out.println("number1

    : " + num); }); processor.subscribe(num -> { System.out.println("number2 : " + num); }); processor.onNext(1); processor.onNext(2); processor.onNext(3); processor.onNext(4); OVNCFS OVNCFS OVNCFS OVNCFS OVNCFS OVNCFS OVNCFS OVNCFS SFTVMU ߪಡొ࿥ ෳ਺ొ࿥Մೳ σʔλొ࿥ 1SPDFTTPSʹ͸ड͚ޱ΋༻ҙ͞Ε͍ͯΔ
  15. ϢʔβʔΞΫγϣϯͰͷ׆༻ྫ RxTextView.textChanges(a1Text) .subscribe(e -> { b1Text.setText( “” + (Long.parseLong(e.text) +

    Long.parseLong(a2Text.getText()))); }); RxView.clicks(button) .debounce(1L, TimeUnit.SECONDS) .subscribe(x -> { // ੍ݶ͞Εͨೖྗ }) 3Y+BWBͷαϯϓϧͱͯ͠ग़ͯདྷΔ͕ ඞਢͱ͍͏Θ͚Ͱ΋ͳ͍ ݸਓతҙݟ
  16. ඪ४ͷ"TZOD5BTL-PBEFSαϯϓϧ public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Bitmap> { //

    ActivityͷͲ͔͜ʹ͋ΔϘλϯλοϓίʔυ button.setOnClickListener(new OnSingleClickListener() {
 @Override
 public void onSingleClick(View view) {
 Bundle args = new Bundle(); args.putString("url", url); // ඇಉظॲཧΛґཔ getSupportLoaderManager().initLoader(0, args, this); } }); @Override public void Loader<Bitmap> onCreateLoader(int id, Bundle args) { return new ImageAsyncTaskLoader(this, args.getString("url")); } @Override public void onLoadFinished(Loader<Bitmap> arg0, Bitmap arg1) { imageView.setImageDrawable(new BitmapDrawable(getResources(), bmp)); } } Ϋϥογϡ͢ΔՄೳੑ͋Γ σʔλऔಘґཔ ݁Ռ$BMMCBDLΛड͚ͯ7JFXߋ৽
  17. "OESPJEΞϓϦͷωοτϫʔΫ௨৴ w ໰୊ - AsyncTaskॲཧͰ͸ਖ਼࣮͘͠૷͢Δίετ͕ߴ͍ - Callback੍ޚ͕ෳࡶʹͳΓ1 ActivityͰ࢖͑ΔAPIΛ૿΍͠ʹ͍͘ • ཧ૝తͳॲཧ

    - ࣮ߦεϨουͷ੍ޚΛ؆୯ʹߦ͍͍ͨ - Activityͷഁغ࣌ʹ͸ߋ৽ॲཧΛࢭΊ͍ͨ - ෳ਺ͷAPIΛݺͼग़͢৔߹ʹ΋͏·͘ॲཧ͍ͨ͠
  18. 3Y+BWBͰ؆ܿʹهࡌ public class MainActivity extends RxActivity { // ActivityͷͲ͔͜ʹ͋ΔϘλϯλοϓίʔυ button.setOnClickListener(()

    -> {
 Flowable.fromCallable(() -> { return userModel.get(userId); }) .compose(bindToLifecycle()) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(user -> { textView.setText(user.getName())); }); } } Ϣʔβʔ৘ใऔಘ "DUJWJUZ͕ഁغ͞ΕͨΒॲཧΛ΍ΊΔ 5ISFBE੍ޚ 7JFXߋ৽
  19. ࠶ར༻ͷͨΊʹ'MPXBCMFʹ͓͖͍ͯͨ͠ public class UserModel { public User get(String userId) {


    …
 } } public class UserModel { public Flowable<User> get(String userId) {
 …
 } } 'MPXBCMFϕʔεͷ໭Γ஋Λ࡞੒
  20. public class MainActivity extends RxActivity { button.setOnClickListener(() -> { userModel.get(userId)


    .flatMap(user -> imageMode.loadBitmap(user.getProfileUrl)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(bmp -> { imageView.setImageDrawable(new BitmapDrawable(getResources(), bmp)); }); } } ࠶ར༻ɿͭͷॲཧΛ߹੒ͯ݁͠ՌΛऔಘ ม׵࣌ʹՄಡੑ͕ߴ·Δ Ϣʔβʔ৘ใΛऔಘͯ͠ ϓϩϑΟʔϧը૾औಘ
  21. ࠶ར༻ɿҰׅऔಘ [JQ public class MainActivity extends RxActivity { button.setOnClickListener(() ->

    { Flowable.zip( userModel.get(userId), imageModel.get(url) )
 .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe((user, bmp) -> { textView.setText(user.name); imageView.setImageDrawable(new BitmapDrawable(getResources(), bmp)); }); } } ෳ਺ͷॲཧΛҰؾʹཁٻͯ͠ ݁Ռ͕ͦΖͬͨஈ֊Ͱ7JFXߋ৽
  22. ϦτϥΠ public class MainActivity extends RxActivity { button.setOnClickListener(() -> {

    userModel.get(userId)
 .flatMap(user -> imageMode.loadBitmap(this.user.profileUrl)) .retry(3, throwable -> { // retryՄ൱ίʔυ return true; }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(bmp -> { imageView.setImageDrawable(new BitmapDrawable(getResources(), bmp)); }); } } qBU.BQͨ͠ॲཧશମ͕Τϥʔ࣌࠶࣮ߦ͞ΕΔ
  23. w ͭલʹྲྀΕͨσʔλΛอ࣋͢Δ#FIBWJPS1SPDFTTPS w ྫ͑͹࠷৽ͷϩάΠϯϢʔβʔσʔλΛΩϟογϡ͓ͯ͘͠ ͳͲͷ࢖͍ํ͕ग़དྷΔ #FIBWJPS 1SPDFTTPS ৽ن7JFX 7JFXߋ৽ TVCTDSJCF

    TVCTDSJCF ࠷৽ͷ݅Λอ࣋ %BUB $IBOHF &WFOU PO/FYU ৽͘͠TVCTDSJCF͞Εͨ΋ͷʹΩϟογϡ͞Εͨ݅Λྲྀ͢ ༷ʑͳ1SPDFTTPS
  24. αʔόαΠυͰͷར༻Ҋ w ϚΠΫϩαʔϏεͰͷ"1*$BMM - ࣮ߦεϨου੍ޚ - Back PressureʹΑΔ੍ޚ - ෳ਺αʔϏεݺͼग़͠ͷ߹੒

    - ϦτϥΠͳͲͷ੍ޚ ϑϩϯτ޲͚"1* Ϣʔβʔ৘ใαʔϏε ը૾৘ใαʔϏε "1*$BMM "1*$BMM "1*$BMM Ґஔ৘ใαʔϏε
  25. 4QSJOHͰ͸3FBDUPS͕࢖ΘΕΔ w 3YʹӨڹΛड͚ͨϦΞΫςΟϒϥΠϒϥϦ 3Y+BWBͱಉ͡ΦϖϨʔλ΋ଟ਺͋Γ w 3FBDUJWF4USFBNTʹ४ڌ 3Y+BWB 3FBDUPS ରԠ+BWBόʔδϣϯ +BWB

    +BWB 0CTFSWBCMFγʔέϯε 'MPXBCMF 'MVY εϨου੍ޚϝιου TVCTDSJCF0O
 PCTFSWF0O TVCTDSJCF0O QVCMJTI0O 3Y+BWBͱ3FBDUPSͰେ͖͘ҧ͏ͱ͜Ζ
  26. 4QSJOH8FC3FBDUJWFͷαϯϓϧ @GetMapping(value = "/github_user", produces = "application/json") public Flux<GithubUser> githubUser()

    { return httpGet("https://api.github.com/users?slice=1") .flatMapMany(response -> response.bodyToFlux(GithubUser.class)) .take(5); } public Mono<ClientResponse> httpGet(String url) { WebClient client = WebClient.create(url); return client.get().uri("").exchange(); } public class GithubUser { public String login; public String avatar_url; public String toString() { return "[login : " + login + ", avator_url : " + avatar_url + "]"; } } > curl http://localhost/github_user [{"login":"mojombo","avatar_url":"https://avatars3.githubusercontent.com/u/1?v=3"}, {"login":"defunkt","avatar_url":"https://avatars3.githubusercontent.com/u/2?v=3"}, {"login":"pjhyett","avatar_url":"https://avatars3.githubusercontent.com/u/3?v=3"}, {"login":"wycats","avatar_url":"https://avatars3.githubusercontent.com/u/4?v=3"}, {"login":"ezmobius","avatar_url":"https://avatars3.githubusercontent.com/u/5?v=3"}] SFTVMU 'MVYϕʔεͷϨεϙϯε 3FBDUJWF8FC$MJFOU
  27. " # $ DPNCJOF-BUFTU w ̎ͭҎ্ ͭҎԼ ͷετϦʔϜΛʮ߹੒ʯ͢Δ w σʔλྲྀΕΔλΠϛϯά͸ετϦʔϜͷͲΕ͔ʹz/FYUz͕ൃߦͨ͞ͱ͖

    w /FYU͕ൃߦ͞Εͳ͔ͬͨํ͸લճྲྀͨ͠σʔλ -BUFTU Λ࠶౓ྲྀ͢ DPNCJOF-BUFTU Y Z lz Y Z     " ߹੒݁Ռ # # $ $
  28. 4FSWFS4FOU&WFOUTͰ߹੒Λ࢖͏ @GetMapping(value = "/stream_data/", produces = "text/event-stream") public Flux<GithubUser> streamData()

    { Flux<Long> interval = Flux.interval(Duration.ofSeconds(1)); Flux<GithubUser> results = httpGet("https://api.github.com/users?slice=1") .flatMapMany(response -> response.bodyToFlux(GithubUser.class)) .take(5); return Flux.zip(interval, results) .map(Tuple2::getT2); } λΠϛϯάΛ͸͔Δ'MVY σʔλΛੜ੒͢Δ'MVY
  29. ॕ"OESPJEͰ,PUMJO͕ਖ਼ࣜαϙʔτʂʂ w 3Y+BWB͸,PUMJOͰ΋࢖͑Δ w 3Y,PUMJO΋͋Δ val list = listOf("Alpha", "Beta",

    "Gamma", "Delta", "Epsilon") list.toObservable() // extension function for Iterables