KEMBAR78
Rxjava 介紹與 Android 中的 RxJava | PDF
RxJava 介紹與
Android 中的 RxJava
⿈黃千碩 (Kros)
oSolve Ltd. / Wish8 Co,. Ltd.
Mobile App Developer
Outline
• RxJava Introduction
• Observable
• Operators
• Subject
• Scheduler
• Android Lifecycle
• Testing
• Performance
What is RxJava?
• RxJava 是 Reactive X (Reactive Extensions) 對
Java VM 的實作
• 是⼀一個 Library,利⽤用資料流 (observable
sequences) 來處理 「asynchronous 與 event-
base 」類型的程式。
• 主要⾓角⾊色為:observable 與 observer 。
What is Reactive?
• 翻譯:「響應式」「反應式」開發
• 把資料 (data) 或是事件 (event) 變成「可觀察」
(observer pattern) 的「資料流」。
• 並加上運算元 (operators) 來操作這些資料。
What is FRP ?
• FRP - Functional Reactive Programming.
• Reactive 是⺫⽬目的
• 為了能讓開發者不落⼊入如何處理(事件)資料的
繁雜程式邏輯中,利⽤用 函數式 (Functional) 的⽅方
法來處理資料流
• filter(), map(), flatMap(), …etc.
Why FRP?
• Concurrency
• thread 的控管複雜
• Asynchronous Programming
• 為追求 60fps,許多事情我們會丟到背景處理
• Callback Hell
• 當 Callback 太多時,眼睛都花了。
– 林信良
“若開發者是以務實且不斷提升作為⾃自我期許,更
⾼高階的抽象化作法將會是必修的課題”
Observable
• Observable 為發射資料的⼈人
Create Observable
• Observable.just()
• Observable.from()
• …etc.
Observable.just()
• 把「資料」轉變成 Observable。
Observable.just("Hello World!")
Observer
• Observer 為對這些資料有興趣的⼈人
• 透過 subscribe method 連結 observer 與 observable.
• Observer 透過 subscribe 來監聽⼀一個 Observable.
Subscribe
• 連結 observable 與 observer
• 通常必須實作 subscribe 的 interface.
• onNext, onError, onComplete
public final Subscription subscribe(final Action1<? super T> onNext,
final Action1<Throwable> onError,
final Action0 onComplete) {
/* ... */
}
>>>>>>>>>>>>>>>>>>> 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() {

}

});
>>>>>>>>>>>>>>>>>>> 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
>>>>>>>>>>>>>>>>>>> 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,讓程式碼更簡潔
Observable.from
• 把「⼀一包資料」轉變成 Observable。⽽而這個
Observable 每次只發射資料中的單⼀一資料
Observable.from(listOfIntegers)
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);

});
“Hot” and “Cold”
Observable
• Observable 什麼時候會發射資料呢?
• Hot observable
• 當它⼀一建⽴立時就會發射資料
• Cold observable
• 當有 observer subscribe 時,才會發射資料
Operators
• Creating Observables (ex: create, from, just, …)
• Transforming Observables (ex: map, flatMap, …)
• Filtering Observables
• Combining Observables
• Error Handling Operators
• Observable Utility Operators
• ……etc.
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 加⼯工
台北市
公廁查詢系統
利⽤用台北市政府 Open Data
http://data.taipei/
⺫⽬目的:找尋離我最近的公廁
http://data.taipei/
Callback 版本
// - 跟 Server 抓取公廁資料
@GET("/apiAccess")

void listToiletCallback(@Query("rid") String rid, 

@Query("scope") String scope,

@Query("limit") int limit,

@Query("offset") int offset,

Callback<ApiResponse> callback);
// - 跟 Server 抓取公廁資料
@GET("/apiAccess")

void listToiletCallback(@Query("rid") String rid, 

@Query("scope") String scope,

@Query("limit") int limit,

@Query("offset") int offset,

Callback<ApiResponse> callback);
@Override

public void onViewCreated(View view, Bundle savedInstanceState) {

super.onViewCreated(view, savedInstanceState);

progressBar.setVisibility(View.VISIBLE);
fetchNearestToilet();
}
// - 跟 Server 抓取公廁資料
@GET("/apiAccess")

void listToiletCallback(@Query("rid") String rid, 

@Query("scope") String scope,

@Query("limit") int limit,

@Query("offset") int offset,

Callback<ApiResponse> callback);
@Override

public void onViewCreated(View view, Bundle savedInstanceState) {

super.onViewCreated(view, savedInstanceState);

progressBar.setVisibility(View.VISIBLE);
fetchNearestToilet();
}
⺫⽬目的:找出距離我 5 km 以內的公廁,
並按照遠近排序
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);

}

});

}
RxJava 版本
// - 跟 Server 抓取公廁資料
@GET("/apiAccess")

Observable<ApiResponse> listToilet(@Query("rid") String rid, 

@Query("scope") String scope, 

@Query("limit") int limit, 

@Query("offset") int offset);
// - 跟 Server 抓取公廁資料
@GET("/apiAccess")

Observable<ApiResponse> listToilet(@Query("rid") String rid, 

@Query("scope") String scope, 

@Query("limit") int limit, 

@Query("offset") int offset);
@Override

public 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));
}
}
// - 跟 Server 抓取公廁資料
@GET("/apiAccess")

Observable<ApiResponse> listToilet(@Query("rid") String rid, 

@Query("scope") String scope, 

@Query("limit") int limit, 

@Query("offset") int offset);
@Override

public 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));
}
}
// - 跟 Server 抓取公廁資料
@GET("/apiAccess")

Observable<ApiResponse> listToilet(@Query("rid") String rid, 

@Query("scope") String scope, 

@Query("limit") int limit, 

@Query("offset") int offset);
@Override

public 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
// - 跟 Server 抓取公廁資料
@GET("/apiAccess")

Observable<ApiResponse> listToilet(@Query("rid") String rid, 

@Query("scope") String scope, 

@Query("limit") int limit, 

@Query("offset") int offset);
@Override

public 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));
}
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);
@Override

public 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));

}
}
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
Subject
• Subject 有很多種:
• AsyncSubject
• BehaviorSubject
• PublishSubject
• ReplaySubject
Publish Subject
• 會發送給每個 observers
• 只會接收到 subscribe 之後的 event
Subject
Activity 1
Subject
Activity 1
Subject
subject.subscribe();
Activity 1
Subject
startActivity();
subject.subscribe();
Activity 1 Activity 2
Subject
startActivity();
subject.subscribe();
Activity 1 Activity 2
Subject
startActivity();
// Do something…
subject.onNext(Event);
finish();
subject.subscribe();
Activity 1 Activity 2
Subject
startActivity();
subject.subscribe();
// Do something…
subject.onNext(Event);
finish();
Activity 1 Activity 2
Subject
startActivity();
subject.subscribe();
// Do something…
subject.onNext(Event);
finish();
Activity 1 Activity 2
Subject
startActivity();
subject.subscribe();
// Do something…
subject.onNext(Event);
finish();
Activity 1 Activity 2
Subject
startActivity();
subject.subscribe();
// Do something…
subject.onNext(Event);
finish();
Activity 2
Subject
startActivity();
subject.subscribe();
// Do something…
subject.onNext(Event);
finish();
Activity 1
(with Event)
Activity 1
(with Event)
Activity 2
Subject
startActivity();
subject.subscribe();
// Do something…
subject.onNext(Event);
finish();
Subject
subject.subscribe();
Activity 1
(with Event)
Subject
subject.subscribe();
Activity 1
(with Event)
Activity 2
(with Event)
Activity 3
(with Event)
subject.subscribe();
subject.subscribe();
可以很多⼈人註冊
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 的切換。
Scheduler
@Override

public 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));

}
}
Android Lifecycle
• Activity 與 Fragment 都有各⾃自的 lifecycle.
• Activity, onCreate(), onResume(), onPause(),
onDestory(), ..etc.
• Fragment, onCreate(), onCreateView(), onResume(),
onPause(), onDestory(), ..etc
• 如果 Activity/Fragment 被 destroy 時,你的 async task
還沒做完怎麼辦?
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
還沒做完怎麼辦?
Android Lifecycle
• Import RxJava Android 版

compile 'io.reactivex:rxandroid:0.25.0'
• 使⽤用 Android 相關的 observable 與 event.

rx.android.lifecycle.LifecycleObservable

rx.android.lifecycle.LifecycleEvent
@Override

protected void onStart() {

super.onStart();

lifecycleSubject.onNext(LifecycleEvent.START);

}



@Override

protected void onResume() {

super.onResume();

lifecycleSubject.onNext(LifecycleEvent.RESUME);

}



@Override

protected void onPause() {

lifecycleSubject.onNext(LifecycleEvent.PAUSE);

super.onPause();

}



@Override

protected void onStop() {

lifecycleSubject.onNext(LifecycleEvent.STOP);

super.onStop();

}



@Override

protected void onDestroy() {

lifecycleSubject.onNext(LifecycleEvent.DESTROY);

super.onDestroy();

}
private final BehaviorSubject<LifecycleEvent> lifecycleSubject = BehaviorSubject.create();
public class BaseActivity extends AppCompatActivity {
}
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 */
}
@Override

public 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));

}
}
@Override

public 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.
@Override

public 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));

}
}
@Override

public 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.
Testing
• 測試容易
• 簡易的⽅方法, toBlocking()
• 正規的⽅方法, TestSubscriber()
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…
}
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());

}
Performance
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());

}
不是每個語⾔言的 Rx 版本效
能都很好,導⼊入時需注意
例如:ReactiveCocoa
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));

}];

}
優缺點
• 優點
• 程式碼清楚,簡潔
• 容易進⾏行 Asynchronous Programming
• 缺點
• 學習成本⾼高(map????, flatMap?????, amb???)
• ⼊入侵式的,所有 API 被迫改成 Observable<T>
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

}
Reference
• ReactiveX

http://reactivex.io/
• FRP與函數式-林信良

http://www.ithome.com.tw/voice/91328
• RxJava Android Patterns

http://stablekernel.com/blog/replace-asynctask-asynctaskloader-rx-
observable-rxjava-android-patterns/
• Architecting Android…The evolution

http://fernandocejas.com/2015/07/18/architecting-android-the-evolution/
• Unit Testing RxJava Observables

https://medium.com/ribot-labs/unit-testing-rxjava-6e9540d4a329
• Demo Project

https://github.com/ch8908/rxjava-demo

Rxjava 介紹與 Android 中的 RxJava

  • 1.
    RxJava 介紹與 Android 中的RxJava ⿈黃千碩 (Kros) oSolve Ltd. / Wish8 Co,. Ltd. Mobile App Developer
  • 2.
    Outline • RxJava Introduction •Observable • Operators • Subject • Scheduler • Android Lifecycle • Testing • Performance
  • 3.
    What is RxJava? •RxJava 是 Reactive X (Reactive Extensions) 對 Java VM 的實作 • 是⼀一個 Library,利⽤用資料流 (observable sequences) 來處理 「asynchronous 與 event- base 」類型的程式。 • 主要⾓角⾊色為:observable 與 observer 。
  • 4.
    What is Reactive? •翻譯:「響應式」「反應式」開發 • 把資料 (data) 或是事件 (event) 變成「可觀察」 (observer pattern) 的「資料流」。 • 並加上運算元 (operators) 來操作這些資料。
  • 5.
    What is FRP? • FRP - Functional Reactive Programming. • Reactive 是⺫⽬目的 • 為了能讓開發者不落⼊入如何處理(事件)資料的 繁雜程式邏輯中,利⽤用 函數式 (Functional) 的⽅方 法來處理資料流 • filter(), map(), flatMap(), …etc.
  • 6.
    Why FRP? • Concurrency •thread 的控管複雜 • Asynchronous Programming • 為追求 60fps,許多事情我們會丟到背景處理 • Callback Hell • 當 Callback 太多時,眼睛都花了。
  • 7.
  • 8.
  • 9.
    Create Observable • Observable.just() •Observable.from() • …etc.
  • 10.
  • 11.
    Observer • Observer 為對這些資料有興趣的⼈人 •透過 subscribe method 連結 observer 與 observable. • Observer 透過 subscribe 來監聽⼀一個 Observable.
  • 12.
    Subscribe • 連結 observable與 observer • 通常必須實作 subscribe 的 interface. • onNext, onError, onComplete public final Subscription subscribe(final Action1<? super T> onNext, final Action1<Throwable> onError, final Action0 onComplete) { /* ... */ }
  • 13.
    >>>>>>>>>>>>>>>>>>> s:Hello World! Observable.just("HelloWorld!").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() {
 }
 });
  • 14.
    >>>>>>>>>>>>>>>>>>> s:Hello World! Observable.just("HelloWorld!").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
  • 15.
    >>>>>>>>>>>>>>>>>>> s:Hello World! Observable.just("HelloWorld!").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,讓程式碼更簡潔
  • 16.
    Observable.from • 把「⼀一包資料」轉變成 Observable。⽽而這個 Observable每次只發射資料中的單⼀一資料 Observable.from(listOfIntegers)
  • 17.
    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);
 });
  • 18.
    “Hot” and “Cold” Observable •Observable 什麼時候會發射資料呢? • Hot observable • 當它⼀一建⽴立時就會發射資料 • Cold observable • 當有 observer subscribe 時,才會發射資料
  • 19.
    Operators • Creating Observables(ex: create, from, just, …) • Transforming Observables (ex: map, flatMap, …) • Filtering Observables • Combining Observables • Error Handling Operators • Observable Utility Operators • ……etc.
  • 20.
    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 加⼯工
  • 21.
  • 22.
  • 23.
  • 24.
    // - 跟Server 抓取公廁資料 @GET("/apiAccess")
 void listToiletCallback(@Query("rid") String rid, 
 @Query("scope") String scope,
 @Query("limit") int limit,
 @Query("offset") int offset,
 Callback<ApiResponse> callback);
  • 25.
    // - 跟Server 抓取公廁資料 @GET("/apiAccess")
 void listToiletCallback(@Query("rid") String rid, 
 @Query("scope") String scope,
 @Query("limit") int limit,
 @Query("offset") int offset,
 Callback<ApiResponse> callback); @Override
 public void onViewCreated(View view, Bundle savedInstanceState) {
 super.onViewCreated(view, savedInstanceState);
 progressBar.setVisibility(View.VISIBLE); fetchNearestToilet(); }
  • 26.
    // - 跟Server 抓取公廁資料 @GET("/apiAccess")
 void listToiletCallback(@Query("rid") String rid, 
 @Query("scope") String scope,
 @Query("limit") int limit,
 @Query("offset") int offset,
 Callback<ApiResponse> callback); @Override
 public void onViewCreated(View view, Bundle savedInstanceState) {
 super.onViewCreated(view, savedInstanceState);
 progressBar.setVisibility(View.VISIBLE); fetchNearestToilet(); } ⺫⽬目的:找出距離我 5 km 以內的公廁, 並按照遠近排序
  • 27.
    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);
 }
 });
 }
  • 28.
  • 29.
    // - 跟Server 抓取公廁資料 @GET("/apiAccess")
 Observable<ApiResponse> listToilet(@Query("rid") String rid, 
 @Query("scope") String scope, 
 @Query("limit") int limit, 
 @Query("offset") int offset);
  • 30.
    // - 跟Server 抓取公廁資料 @GET("/apiAccess")
 Observable<ApiResponse> listToilet(@Query("rid") String rid, 
 @Query("scope") String scope, 
 @Query("limit") int limit, 
 @Query("offset") int offset); @Override
 public 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)); } }
  • 31.
    // - 跟Server 抓取公廁資料 @GET("/apiAccess")
 Observable<ApiResponse> listToilet(@Query("rid") String rid, 
 @Query("scope") String scope, 
 @Query("limit") int limit, 
 @Query("offset") int offset); @Override
 public 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)); } }
  • 32.
    // - 跟Server 抓取公廁資料 @GET("/apiAccess")
 Observable<ApiResponse> listToilet(@Query("rid") String rid, 
 @Query("scope") String scope, 
 @Query("limit") int limit, 
 @Query("offset") int offset); @Override
 public 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
  • 33.
    // - 跟Server 抓取公廁資料 @GET("/apiAccess")
 Observable<ApiResponse> listToilet(@Query("rid") String rid, 
 @Query("scope") String scope, 
 @Query("limit") int limit, 
 @Query("offset") int offset); @Override
 public 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)); }
  • 34.
    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); @Override
 public 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));
 } }
  • 36.
    Subject • 翻譯:主題 • ASubject 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
  • 37.
    Subject • Subject 有很多種: •AsyncSubject • BehaviorSubject • PublishSubject • ReplaySubject
  • 38.
    Publish Subject • 會發送給每個observers • 只會接收到 subscribe 之後的 event
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
    Activity 1 Activity2 Subject startActivity(); subject.subscribe();
  • 44.
    Activity 1 Activity2 Subject startActivity(); // Do something… subject.onNext(Event); finish(); subject.subscribe();
  • 45.
    Activity 1 Activity2 Subject startActivity(); subject.subscribe(); // Do something… subject.onNext(Event); finish();
  • 46.
    Activity 1 Activity2 Subject startActivity(); subject.subscribe(); // Do something… subject.onNext(Event); finish();
  • 47.
    Activity 1 Activity2 Subject startActivity(); subject.subscribe(); // Do something… subject.onNext(Event); finish();
  • 48.
    Activity 1 Activity2 Subject startActivity(); subject.subscribe(); // Do something… subject.onNext(Event); finish();
  • 49.
    Activity 2 Subject startActivity(); subject.subscribe(); // Dosomething… subject.onNext(Event); finish(); Activity 1 (with Event)
  • 50.
    Activity 1 (with Event) Activity2 Subject startActivity(); subject.subscribe(); // Do something… subject.onNext(Event); finish();
  • 51.
  • 52.
    Subject subject.subscribe(); Activity 1 (with Event) Activity2 (with Event) Activity 3 (with Event) subject.subscribe(); subject.subscribe(); 可以很多⼈人註冊
  • 53.
    Scheduler • If youwant 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 的切換。
  • 54.
    Scheduler @Override
 public void onViewCreated(Viewview, 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));
 } }
  • 55.
    Android Lifecycle • Activity與 Fragment 都有各⾃自的 lifecycle. • Activity, onCreate(), onResume(), onPause(), onDestory(), ..etc. • Fragment, onCreate(), onCreateView(), onResume(), onPause(), onDestory(), ..etc • 如果 Activity/Fragment 被 destroy 時,你的 async task 還沒做完怎麼辦?
  • 56.
    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 還沒做完怎麼辦?
  • 57.
    Android Lifecycle • ImportRxJava Android 版
 compile 'io.reactivex:rxandroid:0.25.0' • 使⽤用 Android 相關的 observable 與 event.
 rx.android.lifecycle.LifecycleObservable
 rx.android.lifecycle.LifecycleEvent
  • 58.
    @Override
 protected void onStart(){
 super.onStart();
 lifecycleSubject.onNext(LifecycleEvent.START);
 }
 
 @Override
 protected void onResume() {
 super.onResume();
 lifecycleSubject.onNext(LifecycleEvent.RESUME);
 }
 
 @Override
 protected void onPause() {
 lifecycleSubject.onNext(LifecycleEvent.PAUSE);
 super.onPause();
 }
 
 @Override
 protected void onStop() {
 lifecycleSubject.onNext(LifecycleEvent.STOP);
 super.onStop();
 }
 
 @Override
 protected void onDestroy() {
 lifecycleSubject.onNext(LifecycleEvent.DESTROY);
 super.onDestroy();
 } private final BehaviorSubject<LifecycleEvent> lifecycleSubject = BehaviorSubject.create(); public class BaseActivity extends AppCompatActivity { }
  • 59.
    public class BaseActivityextends 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 */ }
  • 60.
    @Override
 public void onViewCreated(Viewview, 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));
 } } @Override
 public 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.
  • 61.
    @Override
 public void onViewCreated(Viewview, 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));
 } } @Override
 public 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.
  • 62.
    Testing • 測試容易 • 簡易的⽅方法,toBlocking() • 正規的⽅方法, TestSubscriber()
  • 63.
    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… }
  • 64.
    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());
 }
  • 65.
  • 66.
    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());
 }
  • 67.
  • 68.
    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));
 }];
 }
  • 69.
    優缺點 • 優點 • 程式碼清楚,簡潔 •容易進⾏行 Asynchronous Programming • 缺點 • 學習成本⾼高(map????, flatMap?????, amb???) • ⼊入侵式的,所有 API 被迫改成 Observable<T>
  • 70.
    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
 }
  • 71.
    Reference • ReactiveX
 http://reactivex.io/ • FRP與函數式-林信良
 http://www.ithome.com.tw/voice/91328 •RxJava Android Patterns
 http://stablekernel.com/blog/replace-asynctask-asynctaskloader-rx- observable-rxjava-android-patterns/ • Architecting Android…The evolution
 http://fernandocejas.com/2015/07/18/architecting-android-the-evolution/ • Unit Testing RxJava Observables
 https://medium.com/ribot-labs/unit-testing-rxjava-6e9540d4a329 • Demo Project
 https://github.com/ch8908/rxjava-demo