RxAndroid學(xué)習(xí)筆記和總結(jié)
前言
rx系列貌似在前一階段火起來的班利,也是自己接觸的少,一直沒有去學(xué)習(xí)犀变,今天就趁著周六妹孙,腦補一下。
什么是Rx
- Rx是響應(yīng)式編程的意思获枝,本質(zhì)上就是觀察者設(shè)計模式蠢正,是以觀察者(Observer)和訂閱者(Subscriber)為基礎(chǔ)的異步響應(yīng)方式
- 在Android編程的時候,經(jīng)常使用后臺線程省店,那么就可以使用這種方式嚣崭,能夠使得邏輯比較清晰明了(有的人說會增加好多的代碼,但是我覺得代碼的鏈?zhǔn)浇Y(jié)構(gòu)讓代碼看起來更加簡潔明了)
Rx模式以及有點
優(yōu)勢一
- 創(chuàng)建:Rx可以方便的創(chuàng)建事件流和數(shù)據(jù)流
- 組合:Rx使用查詢式的操作符合組合和變換數(shù)據(jù)流
- 監(jiān)聽:Rx可以訂閱任何可觀察的數(shù)據(jù)量并執(zhí)行操作
優(yōu)勢二(簡化代碼)
- 函數(shù)式風(fēng)格:對可觀察數(shù)據(jù)流使用無副作用的輸入流輸出函數(shù)懦傍,避免了程序里面的錯綜復(fù)雜的狀態(tài)
- 簡化代碼:Rx的操作符通潮⒁ǎ可以將復(fù)雜的難題簡化成很少的幾行代碼(配合lambda表達式還能簡化)
- 異步錯誤處理:Rx提供了何時的錯誤處理機制
- 輕松使用并發(fā):Rx的Observables和Schedulers讓開發(fā)著可以很方便的切換UI線程和子線程,擺脫底層的線程同步和各種并發(fā)問題
響應(yīng)式編程
Rx提供了一系列的操作符粗俱,你可以使用它們來過濾(filter)说榆、選擇(select)、變換(transform)源梭、結(jié)合(combine)和組合(compose)多個Observable娱俺,這些操作符讓執(zhí)行和符合變得非常高效。
你可以把Observable當(dāng)做Iterable的推送方式的等價物废麻,使用Iterable荠卷,消費者從生產(chǎn)者那拉取數(shù)據(jù),線程阻塞直至數(shù)據(jù)準(zhǔn)備好烛愧,使用Observable油宜,在數(shù)據(jù)準(zhǔn)備好的時候,生產(chǎn)者將數(shù)據(jù)推送給消費者怜姿,數(shù)據(jù)可以同步或者異步的到達慎冤,方式更加靈活。
RxJava觀察者模式
- 需求:A對象(觀察者)對B對象(被觀察者)的某種變化高度敏感沧卢,需要在B變化的一瞬間做出反應(yīng)蚁堤。
- RxJava四個基本概念
- Observable(被觀察者)
- Observer(觀察者)
- subscribe(訂閱)
- 事件
- Observable和Observer通過subscribe()方法實現(xiàn)訂閱的關(guān)系,從而Observable可以在需要的時候發(fā)出事件來通知Observer但狭。
關(guān)于理論的知識披诗,網(wǎng)上的介紹的太多了撬即,大家可以去看下,在文章的結(jié)尾呈队,我也會附幾篇好的文章剥槐。
手動實現(xiàn)觀察者模式
首先我們需要有觀察者和被觀察者。
被觀察者接口(里面簡單的定義添加觀察者宪摧,移除觀察者粒竖,通知觀察者三個方法)
public interface Watched {
//添加觀察者
public void addWatcher(Watcher watcher);
//移除觀察者
public void removeWatcher(Watcher watcher);
//通知觀察者
public void notifyWathers(String str);
}
觀察者接口(定義更新的方法)
public interface Watcher {
//數(shù)據(jù)變化進行更新
public void update(String str);
}
被觀察者實現(xiàn)類
public class ConcreteWathed implements Watched {
//觀察者
List<Watcher> mList = new ArrayList<>();
@Override
public void addWatcher(Watcher watcher) {
mList.add(watcher);
}
@Override
public void removeWatcher(Watcher watcher) {
mList.remove(watcher);
}
@Override
public void notifyWathers(String str) {
for (Watcher w : mList) {
w.update(str);
}
}
}
觀察者實現(xiàn)類
public class ConcreteWather implements Watcher {
@Override
public void update(String str) {
System.out.println(str);
}
}
測試類
public static void main(String[] args){
Watched watched = new ConcreteWathed();
Watcher watcher1 = new ConcreteWather();
Watcher watcher2 = new ConcreteWather();
Watcher watcher3 = new ConcreteWather();
watched.addWatcher(watcher1);
watched.addWatcher(watcher2);
watched.addWatcher(watcher3);
watched.notifyWathers("I go");
}
輸出結(jié)果
I go
I go
I go
當(dāng)然了,這只是簡單的實現(xiàn)几于,只要曉得原理就行蕊苗,除了自己實現(xiàn),官方也給我們提供了觀察者與被觀察者接口孩革。只要我們?nèi)崿F(xiàn)接口就可以了岁歉。
利用系統(tǒng)提供的類和接口實現(xiàn)觀察者模式
被觀察者
public class XTObservable extends Observable {
private int data = 0;
public int getData(){
return data;
}
public void setData(int i){
if (this.data != i){
this.data = i;
setChanged();//發(fā)生改變
notifyObservers();//通知觀察者
}
}
}
觀察者
public class XTobserver implements Observer {
public XTobserver(XTObservable observable) {
observable.addObserver(this);
}
@Override
public void update(Observable observable, Object o) {
System.out.println("data is changed" + ((XTObservable) observable).getData());
}
}
測試類
public class Test {
public static void main(String[] args) {
XTObservable mObservable = new XTObservable();
XTobserver mXTobserver = new XTobserver(mObservable);
mObservable.setData(1);
mObservable.setData(2);
mObservable.setData(3);
}
}
輸出結(jié)果
data is changed1
data is changed2
data is changed3
上面已經(jīng)手動實現(xiàn)觀察者模式和通過系統(tǒng)提供類實現(xiàn),當(dāng)然這都不是重點膝蜈,重點是Rx響應(yīng)式編程
RxAndroid使用
一:使用前配置
在項目工程的build.gradle文件添加這樣的一句話(如果使用lambda)
classpath 'me.tatarka:gradle-retrolambda:2.5.0'(這一句在gradle版本下面緊接著)
在該module工程的build.gradle文件中添加
apply plugin: 'me.tatarka.retrolambda'(使用lambda)在文件的第二行
在buildTypes節(jié)點的下(不是節(jié)點內(nèi))添加下面一句
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
然后在依賴中添加下面幾句(沒有提示一定添加的可以根據(jù)自己選擇性添加)
//rx一定添加
compile 'io.reactivex:rxjava:1.1.0'
compile 'io.reactivex:rxandroid:1.1.0'
compile 'com.google.code.gson:gson:2.4'
compile 'com.jakewharton:butterknife:7.0.1'
compile 'com.squareup.picasso:picasso:2.5.2'
//添加
compile 'com.squareup.okhttp3:okhttp:3.+'
至此锅移,使用環(huán)境已經(jīng)配置好了,接下來我們來簡單的使用一下饱搏。
利用create創(chuàng)建來使用Rx
/**
* 使用create方式
*/
public static void createObserable() {
//定義被觀察者
Observable<String> observable = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
if (!subscriber.isUnsubscribed()) { //觀察者和被觀察者還有訂閱消息
subscriber.onNext("hello"); //返回的數(shù)據(jù)
subscriber.onNext("hi");
subscriber.onNext(getUserName()); //因為是傳入的是字符串泛型
subscriber.onCompleted(); //完成
}
}
});
//定義觀察者
Subscriber showSub = new Subscriber() {
@Override
public void onCompleted() {
Log.i(TAG, "onCompleted"); //用于對話框消失
}
@Override
public void onError(Throwable e) {
Log.i(TAG, e.getMessage()); //錯誤處理
}
@Override
public void onNext(Object o) {
Log.i(TAG, o.toString());
}
};
observable.subscribe(showSub); //兩者產(chǎn)生訂閱
}
/**
* 可以用來寫成我們的下載返回數(shù)據(jù)
*
* @return
*/
public static String getUserName() {
return "jsonName";
}
在主activity中調(diào)用非剃,我們來看下控制臺輸出的結(jié)果:
也是一個測試,打印
/**
* 打印的功能 鏈?zhǔn)浇Y(jié)構(gòu)推沸,更加易于代碼的可毒性
*/
public static void createPrint() {
Observable.create(new Observable.OnSubscribe<Integer>() {
@Override
public void call(Subscriber<? super Integer> subscriber) {
if (!subscriber.isUnsubscribed()) {
for (int i = 0; i < 10; i++) {
subscriber.onNext(i);
}
subscriber.onCompleted();
}
}
}).subscribe(new Subscriber<Integer>() {
@Override
public void onCompleted() {
Log.i(TAG, "onCompleted");
}
@Override
public void onError(Throwable e) {
Log.i(TAG, e.getMessage());
}
@Override
public void onNext(Integer integer) {
Log.i(TAG, "result--->:" + integer);
}
});
}
看下控制臺結(jié)果
from函數(shù)
/**
* 使用在被觀察者备绽,返回的對象一般都是數(shù)據(jù)類型
* 它接收一個集合作為輸入,然后每次輸出一個元素給subscriber
*/
public static void from() {
Integer[] items = {1, 2, 3, 4, 5, 6, 7, 8};
Observable onservable = Observable.from(items);
onservable.subscribe(new Action1() {
@Override
public void call(Object o) {
Log.i(TAG, o.toString());
}
});
}
控制臺結(jié)果
interval函數(shù)
/**
* 指定某一時刻進行數(shù)據(jù)發(fā)送
* interval()函數(shù)的兩個參數(shù):一個指定兩次發(fā)射的時間間隔鬓催,另一個是用到的時間單位
*/
public static void interval() {
Integer[] items = {1, 2, 3, 4};
Observable observable = Observable.interval(1, 1, TimeUnit.SECONDS);
observable.subscribe(new Action1() {
@Override
public void call(Object o) {
Log.i(TAG, o.toString());
}
});
}
just函數(shù)
/**
* 假如我們只有3個獨立的AppInfo對象并且我們想把他們轉(zhuǎn)化為Observable并填充到RecyclerView的item中:
* 這里我們有兩個數(shù)組肺素,然后通過轉(zhuǎn)化為Observable組成一個item
*/
public static void just() {
Integer[] items1 = {1, 2, 3, 4};
Integer[] items2 = {2, 4, 6, 8};
Observable observable = Observable.just(items1, items2);
observable.subscribe(new Subscriber<Integer[]>() {
@Override
public void onCompleted() {
Log.i(TAG, "onCompleted");
}
@Override
public void onError(Throwable e) {
Log.i(TAG, e.getMessage());
}
@Override
public void onNext(Integer[] integers) {
for (int i = 0; i < integers.length; i++) {
Log.i(TAG, "result--->" + i);
}
}
});
}
輸出結(jié)果:
range函數(shù)
/**
* 指定輸出數(shù)據(jù)的范圍
*/
public static void range() {
Observable observable = Observable.range(1, 4);
observable.subscribe(new Subscriber<Integer>() {
@Override
public void onCompleted() {
Log.i(TAG, "onCompleted");
}
@Override
public void onError(Throwable e) {
Log.i(TAG, e.getMessage());
}
@Override
public void onNext(Integer o) {
Log.i(TAG, "next---->" + o);
}
});
}
輸出結(jié)果:
filter函數(shù)
/**
* 使用過濾功能 發(fā)送消息的時候,先過濾在發(fā)送
*/
public static void filter() {
Observable observable = Observable.just(1, 2, 3, 4, 5, 6);
observable.filter(new Func1<Integer, Boolean>() {
@Override
public Boolean call(Integer o) {
return o < 5;
}
}).observeOn(Schedulers.io()).subscribe(new Subscriber() {
@Override
public void onCompleted() {
Log.i(TAG, "onCompleted");
}
@Override
public void onError(Throwable e) {
Log.i(TAG, e.getMessage());
}
@Override
public void onNext(Object o) {
Log.i(TAG, o.toString());
}
});
}
輸出結(jié)果:
好了宇驾,幾個常用到的函數(shù)已經(jīng)介紹完了倍靡,接下來就用幾個例子來說驗證一下吧。
使用Rx+OkHttp下載圖片
Rx下載的封裝
/**
* 聲明一個被觀察者對象课舍,作為結(jié)果返回
*/
public Observable<byte[]> downLoadImage(String path) {
return Observable.create(new Observable.OnSubscribe<byte[]>() {
@Override
public void call(Subscriber<? super byte[]> subscriber) {
if (!subscriber.isUnsubscribed()) { //存在訂閱關(guān)系
//訪問網(wǎng)絡(luò)操作
//請求體
Request request = new Request.Builder().url(path).get().build();
//異步回調(diào)
mOkHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
subscriber.onError(e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
byte[] bytes = response.body().bytes();
if (bytes != null) {
subscriber.onNext(bytes); //返回結(jié)果
}
}
subscriber.onCompleted(); //訪問完成
}
});
}
}
});
}
在使用的時候調(diào)用
//使用HTTP協(xié)議獲取數(shù)據(jù)
mUtils.downLoadImageOne(url)
.subscribeOn(Schedulers.io()) //在子線程請求
.observeOn(AndroidSchedulers.mainThread()) //結(jié)果返回到主線程這一步很厲害啊塌西,不用我們?nèi)ビ胔andler或者async切換線程了
// 主要我們?nèi)フ{(diào)用一下代碼,就已經(jīng)幫我們切換好了線程筝尾,是不是感覺有點很厲害啊
.subscribe(new Subscriber<byte[]>() {
@Override
public void onCompleted() {
Log.i(TAG,"onCompleted");//對話框消失
}
@Override
public void onError(Throwable e) {
Log.i(TAG,e.getMessage());
}
@Override
public void onNext(byte[] bytes) {
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes,0,bytes.length);
mImageView.setImageBitmap(bitmap);
}
});
Rx+okhttp實現(xiàn)登錄
/**
*
* @param url 登錄地址
* @param params 請求參數(shù)
* @return 后臺返回的數(shù)據(jù)
*/
public Observable<String> login(String url, Map<String, String> params) {
return Observable.create((Observable.OnSubscribe<String>) subscriber -> {
if (!subscriber.isUnsubscribed()) {
//創(chuàng)建formbody
FormBody.Builder builder = new FormBody.Builder();
if (params != null && !params.isEmpty()) {
//循環(huán)獲取body中的數(shù)據(jù)
for (Map.Entry<String, String> entry : params.entrySet()) {
builder.add(entry.getKey(), entry.getValue());
}
}
//請求體
RequestBody requestBody = builder.build();
Request request = new Request.Builder().url(url).post(requestBody).build();
mOkHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
subscriber.onError(e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
//交給觀察者處理數(shù)據(jù)
subscriber.onNext(response.body().string());
}
//完成的回調(diào)
subscriber.onCompleted();
}
});
}
});
}
登錄調(diào)用
Map<String, String> params = new HashMap<String, String>();
params.put("username", userName.getText().toString().trim());
params.put("password", passWord.getText().toString().trim());
mUtils.login(url, params).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<String>() {
@Override
public void onCompleted() {
Log.i(TAG, "onCompleted");
}
@Override
public void onError(Throwable e) {
Log.i(TAG, e.getMessage());
}
@Override
public void onNext(String s) {
if (JsonUtils.parse(s)) {
Intent intent = new Intent(LoginActivity.this, ContentActivity.class);
startActivity(intent);
}
}
});
如果有想需要代碼的捡需,可以看這里,所有代碼已經(jīng)傳至github筹淫。https://github.com/wuyinlei/RxAndroidDemo
好了站辉,就先介紹到這里吧,這里在給大家推薦幾篇比較好的博文還有。
推薦博文
- http://www.devtf.cn/?p=1225
- http://www.cnblogs.com/wangjq/archive/2012/07/12/2587966.html
- http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/1012/3572.html#toc_1
- http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0430/2815.html
推薦git
- https://github.com/wuyinlei/RxDemo
- https://github.com/wuyinlei/ApplicaptionMarket
- https://github.com/androidmalin/RengwuxianRxjava
- https://github.com/rengwuxian/RxJavaSamples
- https://github.com/tough1985/RxjavaRetrofitDemo
結(jié)語
Rx使用還是挺方便的饰剥,不過需要一定的學(xué)習(xí)成本狸相,謹(jǐn)慎使用(嘿嘿)