一:觀察者模式基本實現
1. 創建觀察者Subscriber
Subscriber<String> subscriber = new Subscriber<String>() {
@Override
public void onNext(String s) {
Log.d(tag, "Item: " + s);
}
@Override
public void onCompleted() {
Log.d(tag, "Completed!");
}
@Override
public void onError(Throwable e) {
Log.d(tag, "Error!");
}
};
2. 創建被觀察者Observable ,observable是被觀察者,subscribe是訂閱者,onsbuscribe是訂閱的接口,裏面有個call方法需要實現
Observable observable = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
subscriber.onNext("Hello");
subscriber.onNext("fyc");
subscriber.onCompleted();
}
});
3. 完成訂閱
observable.subscribe(subscriber);
//該方法的源碼是:
public Subscription subscribe(Subscriber subscriber) {
subscriber.onStart();
onSubscribe.call(subscriber); //由此可見觀察者不是在創建的時候就發送請求的,而是在訂閱的時候才call的
return subscriber; //將傳入的 Subscriber 作爲 Subscription 返回。這是爲了方便 unsubscribe().
//另外:public abstract class Subscriber<T> implements Observer<T>, Subscription {}所以這個類可以強轉爲接口
}
4. 快捷創建事件隊列
1> just(T...): 將傳入的參數依次發送出來。
Observable observable = Observable.just("Hello", "Hi", "Aloha")
.subscribe(subscriber);
// 將會依次調用:
// onNext("Hello");
// onNext("Hi");
// onNext("Aloha");
// onCompleted();
2> String[] words = {"Hello", "Hi", "Aloha"};
Observable observable = Observable.from(words)
.subscribe(subscriber);
// 將會依次調用:
// onNext("Hello");
// onNext("Hi");
// onNext("Aloha");
// onCompleted();
5. 除了 subscribe(Observer) 和 subscribe(Subscriber) , subscribe() 還支持不完整定義的回調
Action1<String> onNextAction = new Action1<String>() {
// onNext()
@Override
public void call(String s) {
Log.d(tag, s);
}
};
Action1<Throwable> onErrorAction = new Action1<Throwable>() {
// onError()
@Override
public void call(Throwable throwable) {
// Error handling
}
};
Action0 onCompletedAction = new Action0() {
// onCompleted()
@Override
public void call() {
Log.d(tag, "completed");
}
};
簡單解釋一下這段代碼中出現的 Action1 和 Action0。 Action0 是 RxJava 的一個接口,它只有一個方法 call(),這個方法是無參無返回值的;
由於 onCompleted() 方法也是無參無返回值的,因此 Action0 可以被當成一個包裝對象,將 onCompleted() 的內容打包起來將自己作爲一個參數傳入 subscribe() 以實現不完整定義的回調。
這樣其實也可以看做將 onCompleted() 方法作爲參數傳進了 subscribe(),相當於其他某些語言中的『閉包』。 Action1 也是一個接口,它同樣只有一個方法 call(T param),這個方法也無返回值,但有一個參數;
與 Action0 同理,由於 onNext(T obj) 和 onError(Throwable error) 也是單參數無返回值的,
因此 Action1 可以將 onNext(obj) 和 onError(error) 打包起來傳入 subscribe() 以實現不完整定義的回調。事實上,雖然 Action0 和 Action1 在 API 中使用最廣泛,但 RxJava 是提供了多個 ActionX 形式的接口 (例如 Action2, Action3) 的,它們可以被用以包裝不同的無返回值的方法。
注:正如前面所提到的,Observer 和 Subscriber 具有相同的角色,而且 Observer 在 subscribe() 過程中最終會被轉換成 Subscriber 對象,因此,從這裏開始,後面的描述我將用 Subscriber 來代替 Observer ,這樣更加嚴謹。
6. demo示例
int drawableRes = ...;
ImageView imageView = ...;
Observable.create(new OnSubscribe<Drawable>() {
@Override
public void call(Subscriber<? super Drawable> subscriber) {
Drawable drawable = getTheme().getDrawable(drawableRes));
subscriber.onNext(drawable);
subscriber.onCompleted();
}
})
.subscribeOn(Schedulers.io()) // 指定 subscribe() 發生在 IO 線程
.observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回調發生在主線程
.subscribe(new Observer<Drawable>() {
@Override
public void onNext(Drawable drawable) {
imageView.setImageDrawable(drawable);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Toast.makeText(activity, "Error!", Toast.LENGTH_SHORT).show();
}
});
二:時間調度,實現觀察者模式的異步調度(scheduler:調度器)
1. 調度器分爲如下四種
Schedulers.immediate(): 直接在當前線程運行,相當於不指定線程。這是默認的 Scheduler。
Schedulers.newThread(): 總是啓用新線程,並在新線程執行操作。
Schedulers.io(): I/O 操作(讀寫文件、讀寫數據庫、網絡信息交互等)所使用的 Scheduler。行爲模式和 newThread() 差不多,區別在於 io() 的內部實現是是用一個無數量上限的線程池,可以重用空閒的線程,因此多數情況下 io() 比 newThread() 更有效率。不要把計算工作放在 io() 中,可以避免創建不必要的線程。
Schedulers.computation(): 計算所使用的 Scheduler。這個計算指的是 CPU 密集型計算,即不會被 I/O 等操作限制性能的操作,例如圖形的計算。這個 Scheduler 使用的固定的線程池,大小爲 CPU 核數。不要把 I/O 操作放在 computation() 中,否則 I/O 操作的等待時間會浪費 CPU。
另外, Android 還有一個專用的 AndroidSchedulers.mainThread(),它指定的操作將在 Android 主線程運行。
2. demo示例
Observable.just(1, 2, 3, 4)
.subscribeOn(Schedulers.io()) // 指定 subscribe() 發生在 IO 線程
.observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回調發生在主線程,既會在主線程打印1234
.subscribe(new Action1<Integer>() {
@Override
public void call(Integer number) {
Log.d(tag, "number:" + number);
}
});
三:完成對事件序列的變換(如將string轉換爲bitmap),map與flatMap
1> 獲取所有學生的名字
Student[] students = ...;
Subscriber<String> subscriber = new Subscriber<String>() {
@Override
public void onNext(String name) {
Log.d(tag, name);
}
...
};
Observable.from(students)
.map(new Func1<Student, String>() {
@Override
public String call(Student student) {
return student.getName();
}
})
.subscribe(subscriber);
注意: FuncX 和 ActionX 的區別在 FuncX 包裝的是有返回值的方法。
總結:map可以很容易的實現將一種對象轉換爲另一種對象,是一對一的關係,而flatmap是一對多的關係。
2> 獲取所有學生的所有課程
Student[] students = ...;
Subscriber<Course> subscriber = new Subscriber<Course>() {
@Override
public void onNext(Course course) {
Log.d(tag, course.getName());
}
...
};
Observable.from(students)
.flatMap(new Func1<Student, Observable<Course>>() {
@Override
public Observable<Course> call(Student student) {
return Observable.from(student.getCourses());
}
})
.subscribe(subscriber);
//總結:將所有的事件封在observable中,鋪平後統一分發給訂閱者
四:防抖動
在每次事件觸發後的一定時間間隔內丟棄新的事件。常用作去抖動過濾,例如按鈕的點擊監聽器:
RxView.clickEvents(button) .throttleFirst(500, TimeUnit.MILLISECONDS) // RxBinding 代碼,設置防抖間隔爲 500ms .subscribe(subscriber); 媽媽再也不怕我的用戶手抖點開兩個重複的界面啦。
五:rxjava與retrofit適用場合
- 1> 請求完數據後,需要做進一步的耗時操作,比如從數據庫拿東西時:
a: Callback方式:
getUser(userId, new Callback<User>() {
@Override
public void success(User user) {
new Thread() {
@Override
public void run() {
processUser(user); // 嘗試修正 User 數據
runOnUiThread(new Runnable() { // 切回 UI 線程
@Override
public void run() {
userView.setUser(user);
}
});
}).start();
}
@Override
public void failure(RetrofitError error) {
// Error handling
...
}
};
b: RxJava 的形式
getUser(userId)
.doOnNext(new Action1<User>() {
@Override
public void call(User user) {
processUser(user);
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<User>() {
@Override
public void onNext(User user) {
userView.setUser(user);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable error) {
// Error handling
...
}
});
//可以看出rxjava對後臺進一步耗時操作有專門的處理,doOnNext直接處理
- 2> 兩次網絡請求需要嵌套時:
a: Callback方式:
@GET("/token")
public void getToken(Callback<String> callback);
@GET("/user")
public void getUser(@Query("token") String token, @Query("userId") String userId, Callback<User> callback);
...
getToken(new Callback<String>() {
@Override
public void success(String token) {
getUser(token, userId, new Callback<User>() {
@Override
public void success(User user) {
userView.setUser(user);
}
@Override
public void failure(RetrofitError error) {
// Error handling
...
}
};
}
@Override
public void failure(RetrofitError error) {
// Error handling
...
}
});
b: RxJava 的形式
getToken()
.flatMap(new Func1<String, Observable<User>>() {
@Override
public Observable<User> onNext(String token) {
return getUser(token, userId);
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<User>() {
@Override
public void onNext(User user) {
userView.setUser(user);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable error) {
// Error handling
...
}
});
//可以看出rxjava對於兩次網絡請求不需要兩次的callback,一個flatMap搞定