Contenu connexe Similaire à Rxjava 介紹與 Android 中的 RxJava (20) Rxjava 介紹與 Android 中的 RxJava3. 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 太多時,眼睛都花了。
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,讓程式碼更簡潔
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));
}
}
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
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();
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.
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