Rx學(xué)習(xí)筆記和總結(jié)

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
好了站辉,就先介紹到這里吧,這里在給大家推薦幾篇比較好的博文還有。

推薦博文

推薦git

結(jié)語

Rx使用還是挺方便的饰剥,不過需要一定的學(xué)習(xí)成本狸相,謹(jǐn)慎使用(嘿嘿)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市捐川,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌逸尖,老刑警劉巖古沥,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異娇跟,居然都是意外死亡岩齿,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門苞俘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來盹沈,“玉大人,你說我怎么就攤上這事吃谣∑蚍猓” “怎么了?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵岗憋,是天一觀的道長肃晚。 經(jīng)常有香客問我,道長仔戈,這世上最難降的妖魔是什么关串? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮监徘,結(jié)果婚禮上晋修,老公的妹妹穿的比我還像新娘。我一直安慰自己凰盔,他們只是感情好墓卦,可當(dāng)我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著廊蜒,像睡著了一般趴拧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上山叮,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天著榴,我揣著相機與錄音,去河邊找鬼屁倔。 笑死脑又,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播问麸,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼往衷,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了严卖?” 一聲冷哼從身側(cè)響起席舍,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎哮笆,沒想到半個月后来颤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡稠肘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年福铅,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片项阴。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡滑黔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出环揽,到底是詐尸還是另有隱情略荡,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布歉胶,位于F島的核電站撞芍,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏跨扮。R本人自食惡果不足惜序无,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望衡创。 院中可真熱鬧帝嗡,春花似錦、人聲如沸璃氢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽一也。三九已至巢寡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間椰苟,已是汗流浹背抑月。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留舆蝴,地道東北人谦絮。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓题诵,卻偏偏與公主長得像,于是被迫代替她去往敵國和親层皱。 傳聞我的和親對象是個殘疾皇子性锭,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,302評論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)叫胖,斷路器草冈,智...
    卡卡羅2017閱讀 134,702評論 18 139
  • afinalAfinal是一個android的ioc,orm框架 https://github.com/yangf...
    passiontim閱讀 15,437評論 2 45
  • 也許你會覺得深圳是一座很棒的城市,因為這里接納了萬千年輕人钉赁、有夢想的人在這里拼搏、奮發(fā)携茂、成功你踩、失敗。也許你會覺得深...
    陳左閱讀 250評論 0 0
  • 去爬山吧讳苦,山上有紅紅的果子带膜,還有小火車。星期天鸳谜,幾個好朋友相約膝藕。 那時候不記得放學(xué)后還有作業(yè),我們有無窮的...
    21雪絨花閱讀 465評論 0 1