SlideShare a Scribd company logo
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

More Related Content

What's hot

What's hot (20)

TiDB as an HTAP Database
TiDB as an HTAP DatabaseTiDB as an HTAP Database
TiDB as an HTAP Database
 
Python programming : Threads
Python programming : ThreadsPython programming : Threads
Python programming : Threads
 
ZIO Queue
ZIO QueueZIO Queue
ZIO Queue
 
How to use Map() Filter() and Reduce() functions in Python | Edureka
How to use Map() Filter() and Reduce() functions in Python | EdurekaHow to use Map() Filter() and Reduce() functions in Python | Edureka
How to use Map() Filter() and Reduce() functions in Python | Edureka
 
Lazy java
Lazy javaLazy java
Lazy java
 
Postman. From simple API test to end to end scenario
Postman. From simple API test to end to end scenarioPostman. From simple API test to end to end scenario
Postman. From simple API test to end to end scenario
 
A Scala Corrections Library
A Scala Corrections LibraryA Scala Corrections Library
A Scala Corrections Library
 
Python : Functions
Python : FunctionsPython : Functions
Python : Functions
 
BDD testing with cucumber
BDD testing with cucumberBDD testing with cucumber
BDD testing with cucumber
 
Kotlin Bytecode Generation and Runtime Performance
Kotlin Bytecode Generation and Runtime PerformanceKotlin Bytecode Generation and Runtime Performance
Kotlin Bytecode Generation and Runtime Performance
 
Capabilities for Resources and Effects
Capabilities for Resources and EffectsCapabilities for Resources and Effects
Capabilities for Resources and Effects
 
Test Automation Framework with BDD and Cucumber
Test Automation Framework with BDD and CucumberTest Automation Framework with BDD and Cucumber
Test Automation Framework with BDD and Cucumber
 
Mastering the Sling Rewriter
Mastering the Sling RewriterMastering the Sling Rewriter
Mastering the Sling Rewriter
 
Gradle Introduction
Gradle IntroductionGradle Introduction
Gradle Introduction
 
Everything as Code with Terraform
Everything as Code with TerraformEverything as Code with Terraform
Everything as Code with Terraform
 
Api gateway
Api gatewayApi gateway
Api gateway
 
API Testing following the Test Pyramid
API Testing following the Test PyramidAPI Testing following the Test Pyramid
API Testing following the Test Pyramid
 
給你一個使用 Laravel 的理由
給你一個使用 Laravel 的理由給你一個使用 Laravel 的理由
給你一個使用 Laravel 的理由
 
파이썬 로깅, 끝까지 파보면서 내가 배운 것
파이썬 로깅, 끝까지 파보면서 내가 배운 것파이썬 로깅, 끝까지 파보면서 내가 배운 것
파이썬 로깅, 끝까지 파보면서 내가 배운 것
 
Spring Ldap
Spring LdapSpring Ldap
Spring Ldap
 

Similar to Rxjava 介紹與 Android 中的 RxJava

Nexthink Library - replacing a ruby on rails application with Scala and Spray
Nexthink Library - replacing a ruby on rails application with Scala and SprayNexthink Library - replacing a ruby on rails application with Scala and Spray
Nexthink Library - replacing a ruby on rails application with Scala and Spray
Matthew Farwell
 

Similar to Rxjava 介紹與 Android 中的 RxJava (20)

Building Scalable Stateless Applications with RxJava
Building Scalable Stateless Applications with RxJavaBuilding Scalable Stateless Applications with RxJava
Building Scalable Stateless Applications with RxJava
 
Rx java in action
Rx java in actionRx java in action
Rx java in action
 
Practical RxJava for Android
Practical RxJava for AndroidPractical RxJava for Android
Practical RxJava for Android
 
Tech Talk #4 : RxJava and Using RxJava in MVP - Dương Văn Tới
Tech Talk #4 : RxJava and Using RxJava in MVP - Dương Văn TớiTech Talk #4 : RxJava and Using RxJava in MVP - Dương Văn Tới
Tech Talk #4 : RxJava and Using RxJava in MVP - Dương Văn Tới
 
Practical RxJava for Android
Practical RxJava for AndroidPractical RxJava for Android
Practical RxJava for Android
 
Reactive Extensions for JavaScript
Reactive Extensions for JavaScriptReactive Extensions for JavaScript
Reactive Extensions for JavaScript
 
GKAC 2015 Apr. - RxAndroid
GKAC 2015 Apr. - RxAndroidGKAC 2015 Apr. - RxAndroid
GKAC 2015 Apr. - RxAndroid
 
Reactive programming on Android
Reactive programming on AndroidReactive programming on Android
Reactive programming on Android
 
RxJava2 Slides
RxJava2 SlidesRxJava2 Slides
RxJava2 Slides
 
Rxjava meetup presentation
Rxjava meetup presentationRxjava meetup presentation
Rxjava meetup presentation
 
Reactive Thinking in Java
Reactive Thinking in JavaReactive Thinking in Java
Reactive Thinking in Java
 
JS everywhere 2011
JS everywhere 2011JS everywhere 2011
JS everywhere 2011
 
Nexthink Library - replacing a ruby on rails application with Scala and Spray
Nexthink Library - replacing a ruby on rails application with Scala and SprayNexthink Library - replacing a ruby on rails application with Scala and Spray
Nexthink Library - replacing a ruby on rails application with Scala and Spray
 
Programming proxies to do what we need so we don't have to talk to the networ...
Programming proxies to do what we need so we don't have to talk to the networ...Programming proxies to do what we need so we don't have to talk to the networ...
Programming proxies to do what we need so we don't have to talk to the networ...
 
Scala.js for large and complex frontend apps
Scala.js for large and complex frontend appsScala.js for large and complex frontend apps
Scala.js for large and complex frontend apps
 
Reactive Functional Programming with Java 8 on Android N
Reactive Functional Programming with Java 8 on Android NReactive Functional Programming with Java 8 on Android N
Reactive Functional Programming with Java 8 on Android N
 
Belfast JUG 23-10-2013
Belfast JUG 23-10-2013Belfast JUG 23-10-2013
Belfast JUG 23-10-2013
 
Intro to RxJava/RxAndroid - GDG Munich Android
Intro to RxJava/RxAndroid - GDG Munich AndroidIntro to RxJava/RxAndroid - GDG Munich Android
Intro to RxJava/RxAndroid - GDG Munich Android
 
JavaScript Fundamentals with Angular and Lodash
JavaScript Fundamentals with Angular and LodashJavaScript Fundamentals with Angular and Lodash
JavaScript Fundamentals with Angular and Lodash
 
RxJS Operators - Real World Use Cases - AngularMix
RxJS Operators - Real World Use Cases - AngularMixRxJS Operators - Real World Use Cases - AngularMix
RxJS Operators - Real World Use Cases - AngularMix
 

More from Kros Huang (6)

Android MvRx Framework 介紹
Android MvRx Framework 介紹Android MvRx Framework 介紹
Android MvRx Framework 介紹
 
Kotlin Data Model
Kotlin Data ModelKotlin Data Model
Kotlin Data Model
 
Epoxy 介紹
Epoxy 介紹Epoxy 介紹
Epoxy 介紹
 
Fastlane on Android 介紹
Fastlane on Android 介紹Fastlane on Android 介紹
Fastlane on Android 介紹
 
RxJava 2.0 介紹
RxJava 2.0 介紹RxJava 2.0 介紹
RxJava 2.0 介紹
 
Android with dagger_2
Android with dagger_2Android with dagger_2
Android with dagger_2
 

Recently uploaded

Fruit shop management system project report.pdf
Fruit shop management system project report.pdfFruit shop management system project report.pdf
Fruit shop management system project report.pdf
Kamal Acharya
 
Laundry management system project report.pdf
Laundry management system project report.pdfLaundry management system project report.pdf
Laundry management system project report.pdf
Kamal Acharya
 

Recently uploaded (20)

Fruit shop management system project report.pdf
Fruit shop management system project report.pdfFruit shop management system project report.pdf
Fruit shop management system project report.pdf
 
Cloud-Computing_CSE311_Computer-Networking CSE GUB BD - Shahidul.pptx
Cloud-Computing_CSE311_Computer-Networking CSE GUB BD - Shahidul.pptxCloud-Computing_CSE311_Computer-Networking CSE GUB BD - Shahidul.pptx
Cloud-Computing_CSE311_Computer-Networking CSE GUB BD - Shahidul.pptx
 
Laundry management system project report.pdf
Laundry management system project report.pdfLaundry management system project report.pdf
Laundry management system project report.pdf
 
ENERGY STORAGE DEVICES INTRODUCTION UNIT-I
ENERGY STORAGE DEVICES  INTRODUCTION UNIT-IENERGY STORAGE DEVICES  INTRODUCTION UNIT-I
ENERGY STORAGE DEVICES INTRODUCTION UNIT-I
 
Construction method of steel structure space frame .pptx
Construction method of steel structure space frame .pptxConstruction method of steel structure space frame .pptx
Construction method of steel structure space frame .pptx
 
İTÜ CAD and Reverse Engineering Workshop
İTÜ CAD and Reverse Engineering WorkshopİTÜ CAD and Reverse Engineering Workshop
İTÜ CAD and Reverse Engineering Workshop
 
Furniture showroom management system project.pdf
Furniture showroom management system project.pdfFurniture showroom management system project.pdf
Furniture showroom management system project.pdf
 
HYDROPOWER - Hydroelectric power generation
HYDROPOWER - Hydroelectric power generationHYDROPOWER - Hydroelectric power generation
HYDROPOWER - Hydroelectric power generation
 
The Ultimate Guide to External Floating Roofs for Oil Storage Tanks.docx
The Ultimate Guide to External Floating Roofs for Oil Storage Tanks.docxThe Ultimate Guide to External Floating Roofs for Oil Storage Tanks.docx
The Ultimate Guide to External Floating Roofs for Oil Storage Tanks.docx
 
fluid mechanics gate notes . gate all pyqs answer
fluid mechanics gate notes . gate all pyqs answerfluid mechanics gate notes . gate all pyqs answer
fluid mechanics gate notes . gate all pyqs answer
 
ASME IX(9) 2007 Full Version .pdf
ASME IX(9)  2007 Full Version       .pdfASME IX(9)  2007 Full Version       .pdf
ASME IX(9) 2007 Full Version .pdf
 
BRAKING SYSTEM IN INDIAN RAILWAY AutoCAD DRAWING
BRAKING SYSTEM IN INDIAN RAILWAY AutoCAD DRAWINGBRAKING SYSTEM IN INDIAN RAILWAY AutoCAD DRAWING
BRAKING SYSTEM IN INDIAN RAILWAY AutoCAD DRAWING
 
KIT-601 Lecture Notes-UNIT-3.pdf Mining Data Stream
KIT-601 Lecture Notes-UNIT-3.pdf Mining Data StreamKIT-601 Lecture Notes-UNIT-3.pdf Mining Data Stream
KIT-601 Lecture Notes-UNIT-3.pdf Mining Data Stream
 
Courier management system project report.pdf
Courier management system project report.pdfCourier management system project report.pdf
Courier management system project report.pdf
 
KIT-601 Lecture Notes-UNIT-5.pdf Frame Works and Visualization
KIT-601 Lecture Notes-UNIT-5.pdf Frame Works and VisualizationKIT-601 Lecture Notes-UNIT-5.pdf Frame Works and Visualization
KIT-601 Lecture Notes-UNIT-5.pdf Frame Works and Visualization
 
Top 13 Famous Civil Engineering Scientist
Top 13 Famous Civil Engineering ScientistTop 13 Famous Civil Engineering Scientist
Top 13 Famous Civil Engineering Scientist
 
A CASE STUDY ON ONLINE TICKET BOOKING SYSTEM PROJECT.pdf
A CASE STUDY ON ONLINE TICKET BOOKING SYSTEM PROJECT.pdfA CASE STUDY ON ONLINE TICKET BOOKING SYSTEM PROJECT.pdf
A CASE STUDY ON ONLINE TICKET BOOKING SYSTEM PROJECT.pdf
 
The Benefits and Techniques of Trenchless Pipe Repair.pdf
The Benefits and Techniques of Trenchless Pipe Repair.pdfThe Benefits and Techniques of Trenchless Pipe Repair.pdf
The Benefits and Techniques of Trenchless Pipe Repair.pdf
 
IT-601 Lecture Notes-UNIT-2.pdf Data Analysis
IT-601 Lecture Notes-UNIT-2.pdf Data AnalysisIT-601 Lecture Notes-UNIT-2.pdf Data Analysis
IT-601 Lecture Notes-UNIT-2.pdf Data Analysis
 
Introduction to Machine Learning Unit-5 Notes for II-II Mechanical Engineering
Introduction to Machine Learning Unit-5 Notes for II-II Mechanical EngineeringIntroduction to Machine Learning Unit-5 Notes for II-II Mechanical Engineering
Introduction to Machine Learning Unit-5 Notes for II-II Mechanical Engineering
 

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 太多時,眼睛都花了。
  • 9. Create Observable • Observable.just() • Observable.from() • …etc.
  • 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("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() {
 }
 });
  • 14. >>>>>>>>>>>>>>>>>>> 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
  • 15. >>>>>>>>>>>>>>>>>>> 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,讓程式碼更簡潔
  • 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 加⼯工
  • 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);
 }
 });
 }
  • 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));
 } }
  • 35.
  • 36. 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
  • 37. Subject • Subject 有很多種: • AsyncSubject • BehaviorSubject • PublishSubject • ReplaySubject
  • 38. Publish Subject • 會發送給每個 observers • 只會接收到 subscribe 之後的 event
  • 43. Activity 1 Activity 2 Subject startActivity(); subject.subscribe();
  • 44. Activity 1 Activity 2 Subject startActivity(); // Do something… subject.onNext(Event); finish(); subject.subscribe();
  • 45. Activity 1 Activity 2 Subject startActivity(); subject.subscribe(); // Do something… subject.onNext(Event); finish();
  • 46. Activity 1 Activity 2 Subject startActivity(); subject.subscribe(); // Do something… subject.onNext(Event); finish();
  • 47. Activity 1 Activity 2 Subject startActivity(); subject.subscribe(); // Do something… subject.onNext(Event); finish();
  • 48. Activity 1 Activity 2 Subject startActivity(); subject.subscribe(); // Do something… subject.onNext(Event); finish();
  • 49. Activity 2 Subject startActivity(); subject.subscribe(); // Do something… subject.onNext(Event); finish(); Activity 1 (with Event)
  • 50. Activity 1 (with Event) Activity 2 Subject startActivity(); subject.subscribe(); // Do something… subject.onNext(Event); finish();
  • 52. Subject subject.subscribe(); Activity 1 (with Event) Activity 2 (with Event) Activity 3 (with Event) subject.subscribe(); subject.subscribe(); 可以很多⼈人註冊
  • 53. 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 的切換。
  • 54. 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));
 } }
  • 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 • Import RxJava 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 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 */ }
  • 60. @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.
  • 61. @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.
  • 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());
 }
  • 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());
 }
  • 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