Top Banner
RxJava 介紹與 Android 中的 RxJava 千碩 (Kros) oSolve Ltd. / Wish8 Co,. Ltd. Mobile App Developer
71

Rxjava 介紹與 Android 中的 RxJava

Jan 09, 2017

Download

Engineering

Kros Huang
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Rxjava 介紹與 Android 中的 RxJava

RxJava 介紹與 Android 中的 RxJava

⿈黃千碩 (Kros)oSolve Ltd. / Wish8 Co,. Ltd.

Mobile App Developer

Page 2: Rxjava 介紹與 Android 中的 RxJava

Outline• RxJava Introduction

• Observable

• Operators

• Subject

• Scheduler

• Android Lifecycle

• Testing

• Performance

Page 3: Rxjava 介紹與 Android 中的 RxJava

What is RxJava?

• RxJava 是 Reactive X (Reactive Extensions) 對 Java VM 的實作

• 是⼀一個 Library,利⽤用資料流 (observable sequences) 來處理 「asynchronous 與 event-base 」類型的程式。

• 主要⾓角⾊色為:observable 與 observer 。

Page 4: Rxjava 介紹與 Android 中的 RxJava

What is Reactive?

• 翻譯:「響應式」「反應式」開發

• 把資料 (data) 或是事件 (event) 變成「可觀察」(observer pattern) 的「資料流」。

• 並加上運算元 (operators) 來操作這些資料。

Page 5: Rxjava 介紹與 Android 中的 RxJava

What is FRP ?

• FRP - Functional Reactive Programming.

• Reactive 是⺫⽬目的

• 為了能讓開發者不落⼊入如何處理(事件)資料的繁雜程式邏輯中,利⽤用 函數式 (Functional) 的⽅方法來處理資料流

• filter(), map(), flatMap(), …etc.

Page 6: Rxjava 介紹與 Android 中的 RxJava

Why FRP?• Concurrency

• thread 的控管複雜

• Asynchronous Programming

• 為追求 60fps,許多事情我們會丟到背景處理

• Callback Hell

• 當 Callback 太多時,眼睛都花了。

Page 7: Rxjava 介紹與 Android 中的 RxJava

– 林信良

“若開發者是以務實且不斷提升作為⾃自我期許,更⾼高階的抽象化作法將會是必修的課題”

Page 8: Rxjava 介紹與 Android 中的 RxJava

Observable

• Observable 為發射資料的⼈人

Page 9: Rxjava 介紹與 Android 中的 RxJava

Create Observable

• Observable.just()

• Observable.from()

• …etc.

Page 10: Rxjava 介紹與 Android 中的 RxJava

Observable.just()• 把「資料」轉變成 Observable。

Observable.just("Hello World!")

Page 11: Rxjava 介紹與 Android 中的 RxJava

Observer• Observer 為對這些資料有興趣的⼈人

• 透過 subscribe method 連結 observer 與 observable.

• Observer 透過 subscribe 來監聽⼀一個 Observable.

Page 12: Rxjava 介紹與 Android 中的 RxJava

Subscribe• 連結 observable 與 observer

• 通常必須實作 subscribe 的 interface.

• onNext, onError, onComplete

public final Subscription subscribe(final Action1<? super T> onNext, final Action1<Throwable> onError, final Action0 onComplete) { /* ... */ }

Page 13: Rxjava 介紹與 Android 中的 RxJava

>>>>>>>>>>>>>>>>>>> s:Hello World!

Observable.just("Hello World!").subscribe(new Action1<String>() { @Override public void call(String s) { System.out.println(">>>>>>>>>>>>>>>>>>> s:" + s); } }, new Action1<Throwable>() { @Override public void call(Throwable throwable) { } }, new Action0() { @Override public void call() { } });

Page 14: Rxjava 介紹與 Android 中的 RxJava

>>>>>>>>>>>>>>>>>>> s:Hello World!

Observable.just("Hello World!").subscribe(new Action1<String>() { @Override public void call(String s) { System.out.println(">>>>>>>>>>>>>>>>>>> s:" + s); } }, new Action1<Throwable>() { @Override public void call(Throwable throwable) { } }, new Action0() { @Override public void call() { } });

Observable.just("Hello World!").subscribe(new Action1<String>() { @Override public void call(String s) { System.out.println(">>>>>>>>>>>>>>>>>>> s:" + s); } }); 可以只實作感興趣的 callback

Page 15: Rxjava 介紹與 Android 中的 RxJava

>>>>>>>>>>>>>>>>>>> s:Hello World!

Observable.just("Hello World!").subscribe(new Action1<String>() { @Override public void call(String s) { System.out.println(">>>>>>>>>>>>>>>>>>> s:" + s); } }, new Action1<Throwable>() { @Override public void call(Throwable throwable) { } }, new Action0() { @Override public void call() { } });

Observable.just("Hello World!").subscribe(new Action1<String>() { @Override public void call(String s) { System.out.println(">>>>>>>>>>>>>>>>>>> s:" + s); } });

Observable.just("Hello World!").subscribe(s -> { System.out.println(">>>>>>>>>>>>>>>>>>> s:" + s); });

套⽤用 retrolambda,採⽤用 java8 lambda,讓程式碼更簡潔

Page 16: Rxjava 介紹與 Android 中的 RxJava

Observable.from• 把「⼀一包資料」轉變成 Observable。⽽而這個

Observable 每次只發射資料中的單⼀一資料

Observable.from(listOfIntegers)

Page 17: Rxjava 介紹與 Android 中的 RxJava

Observable.from

>>>>>>>>>>>>>>>>>>> integer:1 >>>>>>>>>>>>>>>>>>> integer:2 >>>>>>>>>>>>>>>>>>> integer:3 >>>>>>>>>>>>>>>>>>> integer:4 >>>>>>>>>>>>>>>>>>> integer:5 >>>>>>>>>>>>>>>>>>> integer:6 >>>>>>>>>>>>>>>>>>> integer:7

List<Integer> integers = new ArrayList<>(); integers.add(1); // ...integers.add(7); Observable.from(integers).subscribe(integer -> { System.out.println(">>>>>>>>>>>>>>>>>>> integer:" + integer); });

Page 18: Rxjava 介紹與 Android 中的 RxJava

“Hot” and “Cold” Observable

• Observable 什麼時候會發射資料呢?

• Hot observable

• 當它⼀一建⽴立時就會發射資料

• Cold observable

• 當有 observer subscribe 時,才會發射資料

Page 19: Rxjava 介紹與 Android 中的 RxJava

Operators• Creating Observables (ex: create, from, just, …)

• Transforming Observables (ex: map, flatMap, …)

• Filtering Observables

• Combining Observables

• Error Handling Operators

• Observable Utility Operators

• ……etc.

Page 20: Rxjava 介紹與 Android 中的 RxJava

Observable.just("Hello World!").map(s -> s + " Android Taipei") .subscribe(s -> { System.out.println(">>>>>>>>>>>>>>>>>>> s:" + s); });

Observable.from(integers) .map(integer -> integer + 10) .subscribe(integer -> { System.out.println(">>>>>>>>>>>>>>>>>>> integer:" + integer); });

>>>>>>>>>>>>>>>>>>> s:Hello World! Android Taipei

>>>>>>>>>>>>>>>>>>> integer:11 >>>>>>>>>>>>>>>>>>> integer:12 >>>>>>>>>>>>>>>>>>> integer:13 >>>>>>>>>>>>>>>>>>> integer:14 >>>>>>>>>>>>>>>>>>> integer:15 >>>>>>>>>>>>>>>>>>> integer:16 >>>>>>>>>>>>>>>>>>> integer:17

對 "Hello World!" 加⼯工

對 list 中每個 element 加⼯工

Page 21: Rxjava 介紹與 Android 中的 RxJava

台北市公廁查詢系統利⽤用台北市政府 Open Data

http://data.taipei/

⺫⽬目的:找尋離我最近的公廁

Page 22: Rxjava 介紹與 Android 中的 RxJava

http://data.taipei/

Page 23: Rxjava 介紹與 Android 中的 RxJava

Callback 版本

Page 24: Rxjava 介紹與 Android 中的 RxJava

// - 跟 Server 抓取公廁資料 @GET("/apiAccess") void listToiletCallback(@Query("rid") String rid, @Query("scope") String scope, @Query("limit") int limit, @Query("offset") int offset, Callback<ApiResponse> callback);

Page 25: Rxjava 介紹與 Android 中的 RxJava

// - 跟 Server 抓取公廁資料 @GET("/apiAccess") void listToiletCallback(@Query("rid") String rid, @Query("scope") String scope, @Query("limit") int limit, @Query("offset") int offset, Callback<ApiResponse> callback);

@Overridepublic void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); progressBar.setVisibility(View.VISIBLE); fetchNearestToilet(); }

Page 26: Rxjava 介紹與 Android 中的 RxJava

// - 跟 Server 抓取公廁資料 @GET("/apiAccess") void listToiletCallback(@Query("rid") String rid, @Query("scope") String scope, @Query("limit") int limit, @Query("offset") int offset, Callback<ApiResponse> callback);

@Overridepublic void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); progressBar.setVisibility(View.VISIBLE); fetchNearestToilet(); }

⺫⽬目的:找出距離我 5 km 以內的公廁,並按照遠近排序

Page 27: Rxjava 介紹與 Android 中的 RxJava

private void fetchNearestToilet() { apiService.listToiletCallback(RID, SCOPE, 500, 0, new Callback<ApiResponse>() { @Override public void success(ApiResponse apiResponse, Response response) { List<Toilet> filtered = new ArrayList<>(); for (Toilet toilet : apiResponse.getResult().getToilets()) { if (lessThan5Km(toilet)) { filtered.add(toilet); } } Collections.sort(filtered, new Comparator<Toilet>() { @Override public int compare(Toilet lhs, Toilet rhs) { return compareDistance(lhs, rhs); } }); adapter.reset(filtered); progressBar.setVisibility(View.GONE); } @Override public void failure(RetrofitError error) { progressBar.setVisibility(View.GONE); ViewHelper.showError(getActivity(), error); } }); }

Page 28: Rxjava 介紹與 Android 中的 RxJava

RxJava 版本

Page 29: Rxjava 介紹與 Android 中的 RxJava

// - 跟 Server 抓取公廁資料 @GET("/apiAccess") Observable<ApiResponse> listToilet(@Query("rid") String rid, @Query("scope") String scope, @Query("limit") int limit, @Query("offset") int offset);

Page 30: Rxjava 介紹與 Android 中的 RxJava

// - 跟 Server 抓取公廁資料 @GET("/apiAccess") Observable<ApiResponse> listToilet(@Query("rid") String rid, @Query("scope") String scope, @Query("limit") int limit, @Query("offset") int offset);

@Overridepublic void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); progressBar.setVisibility(View.VISIBLE); fetchNearestToilet() .observeOn(AndroidSchedulers.mainThread()) .finallyDo(() -> progressBar.setVisibility(View.GONE)) .subscribe((toilets) -> { adapter.reset(toilets); }, throwable -> ViewHelper.showError(getActivity(), throwable)); } }

Page 31: Rxjava 介紹與 Android 中的 RxJava

// - 跟 Server 抓取公廁資料 @GET("/apiAccess") Observable<ApiResponse> listToilet(@Query("rid") String rid, @Query("scope") String scope, @Query("limit") int limit, @Query("offset") int offset);

@Overridepublic void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); progressBar.setVisibility(View.VISIBLE); fetchNearestToilet() .observeOn(AndroidSchedulers.mainThread()) .finallyDo(() -> progressBar.setVisibility(View.GONE)) .subscribe((toilets) -> { adapter.reset(toilets); }, throwable -> ViewHelper.showError(getActivity(), throwable)); } }

Page 32: Rxjava 介紹與 Android 中的 RxJava

// - 跟 Server 抓取公廁資料 @GET("/apiAccess") Observable<ApiResponse> listToilet(@Query("rid") String rid, @Query("scope") String scope, @Query("limit") int limit, @Query("offset") int offset);

@Overridepublic void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); progressBar.setVisibility(View.VISIBLE); fetchNearestToilet() .observeOn(AndroidSchedulers.mainThread()) .finallyDo(() -> progressBar.setVisibility(View.GONE)) .subscribe(adapter::reset, throwable -> ViewHelper.showError(getActivity(), throwable));

} Java 8 的 method reference

Page 33: Rxjava 介紹與 Android 中的 RxJava

// - 跟 Server 抓取公廁資料 @GET("/apiAccess") Observable<ApiResponse> listToilet(@Query("rid") String rid, @Query("scope") String scope, @Query("limit") int limit, @Query("offset") int offset);

@Overridepublic void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); progressBar.setVisibility(View.VISIBLE); fetchNearestToilet() .observeOn(AndroidSchedulers.mainThread()) .finallyDo(() -> progressBar.setVisibility(View.GONE)) .subscribe(adapter::reset, throwable -> ViewHelper.showError(getActivity(), throwable));

}

Page 34: Rxjava 介紹與 Android 中的 RxJava

private Observable<List<Toilet>> fetchNearestToilet() { return apiService.listToilet(RID, SCOPE, 500, 0) .flatMap(response -> Observable.from(response.getResult().getToilets())) .filter(this::lessThan5Km) .toSortedList(this::compareDistance); }

// - 跟 Server 抓取公廁資料 @GET("/apiAccess") Observable<ApiResponse> listToilet(@Query("rid") String rid, @Query("scope") String scope, @Query("limit") int limit, @Query("offset") int offset);

@Overridepublic void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); progressBar.setVisibility(View.VISIBLE); fetchNearestToilet() .observeOn(AndroidSchedulers.mainThread()) .finallyDo(() -> progressBar.setVisibility(View.GONE)) .subscribe(adapter::reset, throwable -> ViewHelper.showError(getActivity(), throwable)); } }

Page 35: Rxjava 介紹與 Android 中的 RxJava
Page 36: Rxjava 介紹與 Android 中的 RxJava

Subject• 翻譯:主題

• A Subject is a sort of bridge or proxy that is available in some implementations of ReactiveX that acts both as an observer and as an Observable.

• Subject 可以是發送 event 的⼈人 (observable),也可以是註冊 event 的⼈人 (observer)。

• ⽤用途:Event Bus

Page 37: Rxjava 介紹與 Android 中的 RxJava

Subject

• Subject 有很多種:

• AsyncSubject

• BehaviorSubject

• PublishSubject

• ReplaySubject

Page 38: Rxjava 介紹與 Android 中的 RxJava

Publish Subject

• 會發送給每個 observers

• 只會接收到 subscribe 之後的 event

Page 39: Rxjava 介紹與 Android 中的 RxJava

Subject

Page 40: Rxjava 介紹與 Android 中的 RxJava

Activity 1

Subject

Page 41: Rxjava 介紹與 Android 中的 RxJava

Activity 1

Subject

subject.subscribe();

Page 42: Rxjava 介紹與 Android 中的 RxJava

Activity 1

Subject

startActivity();

subject.subscribe();

Page 43: Rxjava 介紹與 Android 中的 RxJava

Activity 1 Activity 2

Subject

startActivity();

subject.subscribe();

Page 44: Rxjava 介紹與 Android 中的 RxJava

Activity 1 Activity 2

Subject

startActivity();

// Do something… subject.onNext(Event); finish();

subject.subscribe();

Page 45: Rxjava 介紹與 Android 中的 RxJava

Activity 1 Activity 2

Subject

startActivity();

subject.subscribe();// Do something… subject.onNext(Event); finish();

Page 46: Rxjava 介紹與 Android 中的 RxJava

Activity 1 Activity 2

Subject

startActivity();

subject.subscribe();// Do something… subject.onNext(Event); finish();

Page 47: Rxjava 介紹與 Android 中的 RxJava

Activity 1 Activity 2

Subject

startActivity();

subject.subscribe();// Do something… subject.onNext(Event); finish();

Page 48: Rxjava 介紹與 Android 中的 RxJava

Activity 1 Activity 2

Subject

startActivity();

subject.subscribe();// Do something… subject.onNext(Event); finish();

Page 49: Rxjava 介紹與 Android 中的 RxJava

Activity 2

Subject

startActivity();

subject.subscribe();// Do something… subject.onNext(Event); finish();

Activity 1 (with Event)

Page 50: Rxjava 介紹與 Android 中的 RxJava

Activity 1 (with Event) Activity 2

Subject

startActivity();

subject.subscribe();// Do something… subject.onNext(Event); finish();

Page 51: Rxjava 介紹與 Android 中的 RxJava

Subject

subject.subscribe();

Activity 1 (with Event)

Page 52: Rxjava 介紹與 Android 中的 RxJava

Subject

subject.subscribe();

Activity 1 (with Event)

Activity 2 (with Event)

Activity 3 (with Event)

subject.subscribe();

subject.subscribe();

可以很多⼈人註冊

Page 53: Rxjava 介紹與 Android 中的 RxJava

Scheduler

• If you want to introduce multithreading into your cascade of Observable operators, you can do so by instructing those operators (or particular Observables) to operate on particular Schedulers.

• 可以利⽤用 Scheduler 來實作 thread 的切換。

Page 54: Rxjava 介紹與 Android 中的 RxJava

Scheduler

@Overridepublic void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); progressBar.setVisibility(View.VISIBLE); fetchNearestToilet() .observeOn(AndroidSchedulers.mainThread()) .finallyDo(() -> progressBar.setVisibility(View.GONE)) .subscribe(adapter::reset, throwable -> ViewHelper.showError(getActivity(), throwable)); } }

Page 55: Rxjava 介紹與 Android 中的 RxJava

Android Lifecycle• Activity 與 Fragment 都有各⾃自的 lifecycle.

• Activity, onCreate(), onResume(), onPause(), onDestory(), ..etc.

• Fragment, onCreate(), onCreateView(), onResume(), onPause(), onDestory(), ..etc

• 如果 Activity/Fragment 被 destroy 時,你的 async task 還沒做完怎麼辦?

Page 56: Rxjava 介紹與 Android 中的 RxJava

Android Lifecycle

• 會導致 Memory leak 或是 NPE.

• Activity 與 Fragment 都有各⾃自的 lifecycle.

• Activity, onCreate(), onResume(), onPause(), onDestory(), ..etc.

• Fragment, onCreate(), onCreateView(), onResume(), onPause(), onDestory(), ..etc

• 如果 Activity/Fragment 被 destroy 時,你的 async task 還沒做完怎麼辦?

Page 57: Rxjava 介紹與 Android 中的 RxJava

Android Lifecycle

• Import RxJava Android 版compile 'io.reactivex:rxandroid:0.25.0'

• 使⽤用 Android 相關的 observable 與 event. rx.android.lifecycle.LifecycleObservable rx.android.lifecycle.LifecycleEvent

Page 58: Rxjava 介紹與 Android 中的 RxJava

@Overrideprotected void onStart() { super.onStart(); lifecycleSubject.onNext(LifecycleEvent.START); } @Overrideprotected void onResume() { super.onResume(); lifecycleSubject.onNext(LifecycleEvent.RESUME); } @Overrideprotected void onPause() { lifecycleSubject.onNext(LifecycleEvent.PAUSE); super.onPause(); } @Overrideprotected void onStop() { lifecycleSubject.onNext(LifecycleEvent.STOP); super.onStop(); } @Overrideprotected void onDestroy() { lifecycleSubject.onNext(LifecycleEvent.DESTROY); super.onDestroy(); }

private final BehaviorSubject<LifecycleEvent> lifecycleSubject = BehaviorSubject.create();

public class BaseActivity extends AppCompatActivity {

}

Page 59: Rxjava 介紹與 Android 中的 RxJava

public class BaseActivity extends AppCompatActivity { /* reset code */

public Observable<LifecycleEvent> lifecycle() { return lifecycleSubject.asObservable(); }

protected <T> Observable<T> bind(Observable<T> observable) { return LifecycleObservable.bindActivityLifecycle(lifecycle(), observable.observeOn(AndroidSchedulers.mainThread())); }

/* reset code */ }

Page 60: Rxjava 介紹與 Android 中的 RxJava

@Overridepublic void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); progressBar.setVisibility(View.VISIBLE); bind(fetchNearestToilet()) .finallyDo(() -> progressBar.setVisibility(View.GONE)) .subscribe(adapter::reset, throwable -> ViewHelper.showError(getActivity(), throwable)); } }

@Overridepublic void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); progressBar.setVisibility(View.VISIBLE); fetchNearestToilet() .observeOn(AndroidSchedulers.mainThread()) .finallyDo(() -> progressBar.setVisibility(View.GONE)) .subscribe(adapter::reset, throwable -> ViewHelper.showError(getActivity(), throwable)); } }

bind() 的功能:當 fragment 被 destroyed 時,會⾃自動 unsubscribe 此 observable.

Page 61: Rxjava 介紹與 Android 中的 RxJava

@Overridepublic void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); progressBar.setVisibility(View.VISIBLE); fetchNearestToilet() .observeOn(AndroidSchedulers.mainThread()) .finallyDo(() -> progressBar.setVisibility(View.GONE)) .subscribe(adapter::reset, throwable -> ViewHelper.showError(getActivity(), throwable)); } }

@Overridepublic void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); progressBar.setVisibility(View.VISIBLE); bind(fetchNearestToilet()) .finallyDo(() -> progressBar.setVisibility(View.GONE)) .subscribe(adapter::reset, throwable -> ViewHelper.showError(getActivity(), throwable)); } }

bind() 的功能:當 fragment 被 destroyed 時,會⾃自動 unsubscribe 此 observable.

Page 62: Rxjava 介紹與 Android 中的 RxJava

Testing

• 測試容易

• 簡易的⽅方法, toBlocking()

• 正規的⽅方法, TestSubscriber()

Page 63: Rxjava 介紹與 Android 中的 RxJava

public class AccountDaemon {

public Observable<Account> login(final Account account) { return Observable.just(account).map(account1 -> { checkAccount(account); return accountService.login(account); }); }

private void checkAccount(Account account) throws IllegalArgumentException { if (TextUtils.isEmpty(account.getEmail()) || TextUtils.isEmpty(account.getPassword())) { throw new IllegalArgumentException("Email or password can not be empty."); } }

}

public class Account { private final String email; private final String password;

public static Account createLoginAccount(final String email, final String password) { return new Account(email, password); } // rest implementation… }

Page 64: Rxjava 介紹與 Android 中的 RxJava

public void testLogin_empty_email() throws Exception { Account account = Account.createLoginAccount(null, "password"); try { accountDaemon.login(account).toBlocking().single(); fail("method should throw exception"); } catch (Throwable ex) { assertEquals("Email or password can not be empty.", ex.getLocalizedMessage()); } }

// Official way public void testLogin_using_test_subscriber() { TestSubscriber<Account> testSubscriber = new TestSubscriber<>(); Account account = Account.createLoginAccount("email", "password"); accountDaemon.login(account).subscribe(testSubscriber); Account expect = Account.createLoginAccount("email", "password"); testSubscriber.assertNoErrors(); testSubscriber.assertValue(expect); }

// Blocking way public void testLogin() throws Exception { Account account = Account.createLoginAccount("email", "password"); Account result = accountDaemon.login(account).toBlocking().single(); assertEquals("email", result.getEmail()); assertEquals("password", result.getPassword()); }

Page 65: Rxjava 介紹與 Android 中的 RxJava

Performance

Page 66: Rxjava 介紹與 Android 中的 RxJava

public void testPerformance_rx() { List<Integer> data = new ArrayList<>(); for (int i = 0; i < 100000; ++i) { data.add(i); } List<Integer> result = Observable.from(data) .filter(integer -> integer % 2 == 0).toList().toBlocking().first(); assertEquals(100000 / 2, result.size()); }

public void testPerformance_for_loop() { List<Integer> data = new ArrayList<>(); for (int i = 0; i < 100000; ++i) { data.add(i); } List<Integer> result = new ArrayList<>(); for (int i = 0, size = data.size(); i < size; i++) { if (i % 2 == 0) { result.add(i); } } assertEquals(100000 / 2, result.size()); }

Page 67: Rxjava 介紹與 Android 中的 RxJava

不是每個語⾔言的 Rx 版本效能都很好,導⼊入時需注意

例如:ReactiveCocoa

Page 68: Rxjava 介紹與 Android 中的 RxJava

RAC-ReactiveCocoa

- (void)testRACPerformance { NSArray *array = [self getTestArray]; [self measureBlock:^{ RACSequence *sequence = [array.rac_sequence filter:^BOOL(NSNumber *number) { return number.intValue % 2 == 0; }]; NSArray *results = sequence.array; XCTAssertEqualObjects(@(100000 / 2), @(results.count)); }]; }

- (void)testNativePerformance { NSArray *array = [self getTestArray]; [self measureBlock:^{ NSMutableArray *results = [NSMutableArray array]; for (int i = 0; i < array.count; ++i) { NSNumber *number = array[i]; if (number.intValue % 2 == 0) { [results addObject:number]; } } XCTAssertEqualObjects(@(100000 / 2), @(results.count)); }]; }

Page 69: Rxjava 介紹與 Android 中的 RxJava

優缺點• 優點

• 程式碼清楚,簡潔

• 容易進⾏行 Asynchronous Programming

• 缺點

• 學習成本⾼高(map????, flatMap?????, amb???)

• ⼊入侵式的,所有 API 被迫改成 Observable<T>

Page 70: Rxjava 介紹與 Android 中的 RxJava

public void fetchUserProfile() { // code} public void fetchFriends() { // code} public void fetchShippingInfo() { // code}

public Observable<Profile> fetchUserProfile() { // code} public Observable<List<Friend>> fetchFriends() { // code} public Observable<ShippingInfo> fetchShippingInfo() { // code}

Page 71: Rxjava 介紹與 Android 中的 RxJava

Reference• ReactiveX

http://reactivex.io/

• FRP與函數式-林信良 http://www.ithome.com.tw/voice/91328

• RxJava Android Patternshttp://stablekernel.com/blog/replace-asynctask-asynctaskloader-rx-observable-rxjava-android-patterns/

• Architecting Android…The evolutionhttp://fernandocejas.com/2015/07/18/architecting-android-the-evolution/

• Unit Testing RxJava Observableshttps://medium.com/ribot-labs/unit-testing-rxjava-6e9540d4a329

• Demo Projecthttps://github.com/ch8908/rxjava-demo