Rxjava2的簡單使用與基本操作符

weixin_34320159發表於2018-02-03

一 、關於Rxjava

非同步:RxJava 在 GitHub 主頁上的自我介紹是 "a library for composing asynchronous and event-based programs using observable sequences for the Java VM"(一個在 Java VM 上使用可觀測的序列來組成非同步的、基於事件的程式的庫)。
簡潔:非同步操作很關鍵的一點是程式的簡潔性,因為在排程過程比較複雜的情況下,非同步程式碼經常會既難寫也難被讀懂。RxJava 的優勢也是簡潔,但它的簡潔的與眾不同之處在於,隨著程式邏輯變得越來越複雜,它依然能夠保持簡潔。

7756923-e391b1308b9b5576.png

二 、基本概念

  • Observable:發射源,在觀察者模式中稱為被觀察者
  • Observer:接收源,在觀察者模式中成為觀察者,可接收Observable、Subject發射的資料;
  • Subscriber:“訂閱者”,也是接收源,接收源在觀察者模式中成為觀察者。Subscriber實現了Observer介面,比Observer多了一個最重要的方法unsubscribe( ),用來取消訂閱。
  • subscribe:訂閱,Observable和Observer通過subscribe()進行訂閱
  • Subscription:Observable呼叫subscribe( )方法返回的物件,同樣有unsubscribe( )方法,可以用來取消訂閱事件;
  • Disposable:用於維繫觀察者、被觀察者之間的聯絡。
  • Event:事件。

1、基本呼叫

新增依賴

implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
implementation 'io.reactivex.rxjava2:rxjava:2.2.3'

建立被觀察者

Observable<String> normalObservable  = Observable.create(new ObservableOnSubscribe<String>() {
    @Override
    public void subscribe(ObservableEmitter<String> observableEmitter) throws Exception {
        observableEmitter.onNext("msg1");//通過onNext(),發射一個"msg1"的String
        observableEmitter.onNext("msg2");//通過onNext(),發射一個"msg2"的String
        observableEmitter.onComplete();//發射完成,這種方法需要手動呼叫onCompleted,才會回撥Observer的onCompleted方法
    }
});

建立觀察者

Observer<String> mObserver = new Observer<String>() {
    @Override
    public void onSubscribe(Disposable disposable) {
        //d.dispose();移除訂閱關係
        //d.isDisposed()是否發生訂閱關係
    }
    @Override
    public void onNext(String s) {
    }
    @Override
    public void onError(Throwable throwable) {
    }
    @Override
    public void onComplete() {
    }
}

呼叫subscribe實現訂閱

normalObservable.subscribe(mObserver);

2、鏈式呼叫

當然我們也可以使用鏈式操作的寫法
其中Consumer引數的方法表示下游只對我們關心onNext事件,或Throwable事件進行處理

Observable.create(new ObservableOnSubscribe<String>() {
    @Override
    public void subscribe(ObservableEmitter<String> observableEmitter) throws Exception {
    }
}).subscribe(new Consumer<String>() {
    @Override
    public void accept(String s) throws Exception {
    }
});

三、執行緒切換

我們在請求網路時必須是將其放在子執行緒執行,然後在安卓主執行緒中更新Ui

程式碼 含義
Schedulers.immediate 直接在當前執行緒執行。
Schedulers.newThread 啟用新執行緒,並線上程執行操作。
Schedulers.io 內部是一個無數量上限的的執行緒池,可以重用空閒的執行緒,不要把計算工作放在io中。
Schedulers.computation 使用固定的執行緒池,大小為CPU核數。
  • subscribeOn():指定Observable執行緒

    subscribeOn(Schedulers.io())在IO執行緒中請求網路

  • observeOn():指定Observer執行緒

    observeOn(AndroidSchedulers.mainThread())在主執行緒中更新介面

Observable.create(new ObservableOnSubscribe<Resp>() {
     @Override
     public void subscribe(ObservableEmitter<Resp> e) throws Exception {
          //模擬登陸
        Call<Resp> respCall = api.login(new User(username, password));
        Resp resp = respCall.execute().body();
        e.onNext(resp);
    }
})
//設定請求網路在io執行緒內執行、子執行緒中執行
.subscribeOn(Schedulers.io())
//設定更新ui在安卓主執行緒中執行
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<String>() {
    @Override
    public void accept(String s) throws Exception {
        //更新UI
    }
});

四、操作符

操作符變換符是RxJava中一個很重要的概念,也是其簡潔很重要的原因,這裡整理一些很常見的操作符。

》》1.使用just( ),建立一個Observable並自動為你呼叫onNext( )發射資料(最多可傳送9個)

Observable  justObservable = Observable.just("just1","just2");//依次傳送"just1"和"just2"

String []justs={"just1","just2"};
Observable.fromArray(justs)//傳入陣列,類似於just();

》》2.使用from( ),遍歷集合,傳送每個item

List<String> list = new ArrayList<>();
list.add("from1");
list.add("from2");
list.add("from3");
Observable  fromObservable = Observable.from(list);  //遍歷list 每次傳送一個

注意:just()方法也可以傳list,但是傳送的是整個list物件,而from()傳送的是list的一個item
》》3.使用defer( ),有觀察者訂閱時才建立Observable,並且為每個觀察者建立一個新的Observable

Observable  deferObservable = Observable.defer(new Func0<Observable<String>>() {
    @Override
    //注意此處的call方法沒有Subscriber引數
    public Observable<String> call() {
        return Observable.just("deferObservable");
    }});

》》4.使用interval( )建立一個按固定時間間隔發射整數序列的Observable,可用作定時器

Observable  intervalObservable = Observable.interval(1, TimeUnit.SECONDS);//每隔一秒傳送一次

》》5.使用range( ),建立一個發射特定整數序列的Observable,第一個引數為起始值,第二個為傳送的個數,如果為0則不傳送,負數則拋異常

Observable rangeObservable = Observable.range(10, 5);//將傳送整數10,11,12,13,14

》》6.使用timer( ),建立一個Observable,它在一個給定的延遲後發射一個特殊的值,等同於Android中Handler的postDelay( )方法

Observable timeObservable = Observable.timer(3, TimeUnit.SECONDS);  //3秒後發射一個值

》》7.使用repeat( ),建立一個重複發射特定資料的Observable

Observable repeatObservable = Observable.just("repeatObservable").repeat(3);//重複發射3次

》》8.使用concat( ),連線兩個被訂閱者,訂閱者將會按照a->b的順序收到兩個被訂閱者所發射的訊息。

final String[] aStrings = {"A1", "A2", "A3", "A4"};
final String[] bStrings = {"B1", "B2", "B3"};
final Observable<String> aObservable = Observable.fromArray(aStrings);
final Observable<String> bObservable = Observable.fromArray(bStrings);
Observable.concat(aObservable, bObservable);

輸出A1", "A2", "A3", "A4","B1", "B2", "B3"

》》9.使用window( ),每隔n秒,發射這段時間內的資料,不是有資料就發射

Observable windowObservable=Observable.interval(1, TimeUnit.SECONDS).window(3, TimeUnit.SECONDS);//3秒後,發射前三秒所發射的資料

五、變換操作符

》》1. Map:最常用且最實用的操作符之一,將物件轉換成另一個物件發射出去,應用範圍非常廣,如資料的轉換,資料的預處理等。(如我們傳入使用者id需要查詢使用者資訊,我們就可以使用map建立id返回user物件)
例一:資料型別轉換,改變最終的接收的資料型別。假設傳入本地圖片路徑,根據路徑獲取圖片的Bitmap。

Observable.just(filePath).map(new Func1<String, Bitmap>() {
    @Override
    public Bitmap call(String path) {
         return getBitmapByPath(path);
    }}).subscribe(new Action1<Bitmap>() {
     @Override
    public void call(Bitmap bitmap) {
    //獲取到bitmap,顯示
}});

例二:對資料進行預處理,最後得到理想型資料。實際開發過程中,從後臺介面獲取到的資料也許不符合我們想要的,這時候可以在獲取過程中對得到的資料進行預處理(結合Retrofit)。

Observable.just("12345678").map(new Func1<String, String>() {
    @Override
    public String call(String s) {
        return s.substring(0,4);//只要前四位
    }})
.subscribe(new Action1<String>() {
    @Override
    public void call(String s) {
        Log.i("mytag",s);
    }});

》》2. FlatMap:和Map很像但又有所區別,Map只是轉換髮射的資料型別,而FlatMap可以將原始Observable轉換成另一個Observable。
例:
需要使用的school類,student類就不展示了,儲存一些學生基本資訊的欄位

public class School {
    private String name;
    private List<Student> studentList;
    ......
    class studengt{
    ......
    }
}
List<School> schoolList = new ArrayList<>();

首先假設要列印全國所有學校的名稱,可以直接用Map:

Observable.from(schoolList).map(new Func1<School, String>() {
    @Override
    public String call(School school) {
          return school.getName();
    }}).subscribe(new Action1<String>() {
    @Override
    public void call(String schoolName) {
          Log.i(TAG,schoolName);
    }});

再進一步,列印學校所有學生的姓名,先使用map

Observable.from(schoolList).map(new Func1<School, School.Student>() {
    @Override
    public School.Student call(School school) {
        return school.getStudentList();//錯誤的地方
    }}).subscribe(new Action1<School.Student>() {
    @Override
    public void call(School.Student student) {
            Log.i(TAG,student.getName());
    }});

看似可行,但事實上,這是一段錯誤的程式碼,細心的人就會發現錯誤的地方 school.getStudentList()返回的時list集合
Map是一對一的關係,無法將單一的School物件轉變成多個Student。FlatMap可以改變原始Observable變成另外一個Observable,如果我們能利用from()操作符把school.getStudentList()變成另外一個Observable,現在使用FlatMap實現

Observable.from(schoolList).flatMap(new Func1<School, Observable<School.Student>>() {
    @Override
    public Observable<School.Student> call(School school) {

        return Observable.from(school.getStudentList()); //關鍵,將學生列表以另外一個Observable發射出去

    }}).subscribe(new Action1<School.Student>() {

    @Override
    public void call(School.Student student) {
        Log.i(TAG,student.getName());
    }});

》》3. Buffer:快取,可以設定快取大小,快取滿後,以list的方式將資料傳送出去;例:

Observable.just(1,2,3).buffer(2).subscribe(new Action1<List<Integer>>() {
    @Override
    public void call(List<Integer> list) {
        Log.i(TAG"size:"+list.size());
    }});

執行列印結果如下:

MainActivity.this: size:2
MainActivity.this: size:1

BufferMap經常一起使用,通常發生在從後臺取完資料,對一個List中的資料進行預處理後,再用Buffer快取後一起傳送,保證最後資料接收還是一個List,如:

List<School> schoolList = new ArrayList<>();
Observable.from(schoolList).map(new Func1<School, School>() {
    @Override
    public School call(School school) {
        school.setName("NB大學");  //將所有學校改名
        return school;
    }}).buffer(schoolList.size())  //快取起來,最後一起傳送
.subscribe(new Action1<List<School>>() {
    @Override
    public void call(List<School> schools) {   
}});

六 、過濾操作符

》》1.Take:發射前n項資料,還是用上面的例子,假設不要改所有學校的名稱了,就改前四個學校的名稱:

Observable.from(schoolList).take(4).map(new Func1<School, School>() {
    @Override
    public School call(School school) {
        school.setName("NB大學");
        return school;
    }}).buffer(4).subscribe(new Action1<List<School>>() {
    @Override
    public void call(List<School> schools) {
    }});

》》2.Distinct:去掉重複的項,比較好理解

Observable.just(1, 2, 1, 1, 2, 3)
        .distinct()
        .subscribe(new Action1<Integer>() {
            @Override
            public void call(Integer item) {
                System.out.println("Next: " + item);
            }
        });

輸出

Next: 1
Next: 2
Next: 3

》》3.Filter:過濾,通過謂詞判斷的項才會被髮射,例如,發射小於4的資料

Observable.just(1, 2, 3, 4, 5)
        .filter(new Func1<Integer, Boolean>() {
            @Override
            public Boolean call(Integer item) {
                return( item < 4 );
            }
        }).subscribe(new Action1<Integer>() {
          @Override
          public void call(Integer item) {
                System.out.println("Next: " + item);
      }});

輸出:

Next: 1
Next: 2
Next: 3

》》window:

關於其他操作符或詳情檢視官網:RxJava使用以及操作符

六、注意事項

1、RxBinding的使用

RxBinding是在RxJava的基礎上封裝的一些操作,可以處理常用的一些UI的響應問題,這裡具體實現就不分析了,只整理一些常用的方法作為記錄。

新增依賴:

implementation 'com.jakewharton.rxbinding3:rxbinding:3.0.0-alpha1'

1.1、常用方法:

1.1.1、RxView

  • RxView.clicks().throttleFirst(long windowDuration, TimeUnit unit)指定的時間windowDuration內,點選clicks事件只響應一次
  • RxView.longClicks()長按監聽
  • RxView.draws()繪製監聽
  • RxView.drags()拖拽監聽
  • RxView.scrollChangeEvents()滑動觸發
  • .....

例:按鈕防抖,指定時間內事件只響應1次

//2秒內,按鈕點選事件只響應1次
RxView.clicks(btn)
    .throttleFirst(2, TimeUnit.SECONDS)
    .subscribe(new Consumer<Object>() {
        @Override
        public void accept(Object o) throws Exception {
         }
    }

1.1.2、RxTextView

  • RxTextView.textChanges()EditText輸入監聽
  • RxTextView.textChangeEvents()封裝了TextWatcher文字改變的監聽,返回資料的型別為TextViewTextChangeEvent,內部包含詳細的文字改變資料。
  • RxTextView.editorActions()監聽了軟鍵盤的回車點選
  • RxTextView.editorActionEvents()監聽了軟鍵盤的回車點選,返回型別為TextViewEditorActionEvent。
  • ......

例:監聽文字變化

RxTextView.textChanges(et)
    .subscribe(new Consumer<CharSequence>() {
        @Override
        public void accept(CharSequence charSequence) throws Exception {
        }
    );

1.1.3、RxCompoundButton

  • RxCompoundButton.checkedChanges()選中狀態改變事件

  • .....

    RxView.clicks(btnLogin)
        .subscribe(o -> {
            RxCompoundButton.checked(cb).accept(true);
        }));
    RxCompoundButton.checkedChanges(cb)
        .subscribe(aBoolean -> {
           ......
    });
    

2、避免記憶體洩漏

Activity被銷燬時,我們的後臺任務沒有執行完,那麼就會導致Activity不能正常回收,而對於每一個Observer,都會有一個Disposable物件用於管理。

ObserveronSubscribe回撥中,會傳入一個Disposable物件,下游可以通過該物件的dispose()方法主動切斷和上游的聯絡,在這之後上游的observableEmitter.isDisposed()方法將返回true。當上遊和下游的聯絡切斷之後,下游收不到包括onComplete/onError在內的任何事件,若此時上游再呼叫onError方法傳送事件,那麼將會報錯。

為避免造成記憶體洩漏,我們需要將其將入到該集合當中,在Activity的onDestroy方法中,呼叫它的clear方法,就能避免記憶體洩漏的發生。

public class MainActivity extends AppCompatActivity {
  private static final String TAG = "MainActivity";
  private CompositeDisposable compositeDisposable;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);

      compositeDisposable=new CompositeDisposable();
      Observable<String> observable = Observable.create(new ObservableOnSubscribe<String>() {
          @Override
          public void subscribe(ObservableEmitter<String> observableEmitter) throws Exception {
              observableEmitter.onNext("msg");
          }
      });
      DisposableObserver<String> disposableObserver = new DisposableObserver<String>() {
          @Override
          public void onNext(String s) {
              Log.i(TAG, "onNext: "+s);
          }
          @Override
          public void onError(Throwable throwable) {
          }
          @Override
          public void onComplete() {
          }
      };
      observable.subscribe(disposableObserver);
      compositeDisposable.add(disposableObserver);
  }

  @Override
  protected void onDestroy() {
      compositeDisposable.clear();
      super.onDestroy();
  }
}

相關文章