RxJava2『使用』

基礎概念

RxJava是一個在基于Java VM擴展的使用可觀測的序列來組成異步的、基于事件的程序的庫。它基于觀察者模式支持數據和事件的流式流通處理,并增加了多種運算符以在多種場景下整合事件和數據。抽象了低級別的線程、同步、并發API。

  • 響應式編程(Reactive Programming)是一種面向數據流和變化傳播的編程范式。這意味著可以在編程語言中很方便地表達靜態或動態的數據流,而相關的計算模型會自動將變化的值通過數據流進行傳播
  • 函數式編程是也一種編程范式,它將函數視為一等公民,意味著函數和其他類型數據有同等的地位,可以作為函數的參數、返回值使用(高階函數)。
  • 被觀察者,Observable/Flowable
  • 觀察者,Observer/Consumer/Subscriber
  • 上游:發射事件的地方,對應的是被訂閱者(Observable/Flowable)
  • 下游:接收事件的地方,對應的是觀察者(Observer/Subscriber)

使用

1. 創建被觀察者(上游對象,Observable/Flowable)
Observable<String> observable = Observable.fromArray(new String[]{"A","B","C"});
2.創建觀察者(下游對象,Observer/Consumer)
//創建一個Observer
Observer<String> observer = new Observer<String>() {
    @Override
    public void onSubscribe(Disposable d) {
    }
    @Override
    public void onNext(String s) {
        System.out.println("Thread:" + Thread.currentThread() + "|" + "accept:"+ s);
    }
    @Override
    public void onError(Throwable e) {
    }
    @Override
    public void onComplete() {
    }
};
//創建一個Consumer
Consumer<String> consumer = new Consumer<String>() {
    @Override
    public void accept(String s) throws Exception {
        System.out.println("Thread:" + Thread.currentThread() + "|" + "onNext:"+ s);
    }
};

Consumer和Observer的不同之處在于Consumer不關心onNext()和onError()方法,它只管處理上游發送的數據。

3.訂閱

訂閱通過方法subscribe()實現,當一個被觀察者收到一個訂閱請求時,他就會開始發送一遍事件。所以下面2次subscribe動作,observable發送了2遍事件。

observable.subscribe(observer);
observable.subscribe(consumer);
>>>輸出:
Thread:Thread[main,5,main]|onNext:A
Thread:Thread[main,5,main]|onNext:B
Thread:Thread[main,5,main]|onNext:C
Thread:Thread[main,5,main]|accept:A
Thread:Thread[main,5,main]|accept:B
Thread:Thread[main,5,main]|accept:C
4.使用調度器(Schedulers)指定上下游執行線程

在開發過程中,我們經常需要把一些耗時動作放在后臺線程中進行,然后將結果放到主線程中處理。

在RxJava中可以使用調用器的subscribeOn指定在哪個線程發生訂閱(及上游)observeOn用于指定在哪個線程發生觀察(即下游)從而實現這樣的功能。我們先把上游指定到newThread中執行,任務為每隔一秒發送一個數字。

Observable.create(new ObservableOnSubscribe<String>() {
    @Override
    public void subscribe(ObservableEmitter<String> emitter) throws Exception {
        for (int i = 0; i < 3; i++) {
            System.out.println("Thread:" + Thread.currentThread() + "|" + "emit:" + i);
            emitter.onNext(i + "");
            Thread.sleep(1000);
        }
    }
}).subscribeOn(Schedulers.newThread()).subscribe(new Consumer<String>() {
    @Override
    public void accept(String s) throws Exception {
        System.out.println("Thread:" + Thread.currentThread() + "|" + "accept:"+ s);
    }
});
>>>輸出:

Process finished with exit code 0

奇怪的是根據console看到上游發送任何事件,下游更沒有接收到任何事件。這是因為:

在RxJava中默認的調度器運行在守護線程上這意味著只要Java主線程退出,所有這些默認的調度器會停止。本段代碼中上游運行在子線程中處理耗時操作,下游運行在主線程中,上游還沒開始發送消息,下游的主線程已經結束任務了,所以調度器來不及調度也就停止了。現在我們讓上游運行在默認的主線程中,下游運行在子線程中:

Observable.create(new ObservableOnSubscribe<String>() {
    @Override
    public void subscribe(ObservableEmitter<String> emitter) throws Exception {
        for (int i = 0; i < 3; i++) {
            System.out.println("Thread:" + Thread.currentThread() + "|" + "emit:" + i);
            emitter.onNext(i + "");
            Thread.sleep(1000);
        }
    }
}).observeOn(Schedulers.newThread()).subscribe(new Consumer<String>() {
    @Override
    public void accept(String s) throws Exception {
        System.out.println("Thread:" + Thread.currentThread() + "|" + "accept:"+ s);
    }
});
>>>輸出:
Thread:Thread[main,5,main]|emit:0
Thread:Thread[RxNewThreadScheduler-1,5,main]|accept:0
Thread:Thread[main,5,main]|emit:1
Thread:Thread[RxNewThreadScheduler-1,5,main]|accept:1
Thread:Thread[main,5,main]|emit:2
Thread:Thread[RxNewThreadScheduler-1,5,main]|accept:2
5.結合操作符實現數據操作

我們知道RxJava有多種操作符可以操作流中的數據,下面是一個例子,假設學生的姓名和成績來自兩個接口,我們需要等這個接口都返回結果時,才刷新界面,看用下面這demo代碼,需要注意將2個接口請求放到子線程中,否則2個模擬請求默認會在主線程中,進行串行調用。(可以看兩個log的時間間隔)

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import java.util.HashMap;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.BiFunction;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;

public class TestOperatorActivity extends Activity {

    private String TAG = "TestOP";

    class Stu {
        String name;
        int scoreMath, scoreEnglish;

        public Stu(String name, int scoreMath, int scoreEnglish) {
            this.name = name;
            this.scoreMath = scoreMath;
            this.scoreEnglish = scoreEnglish;
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.rx_act_test_operator);
        findViewById(R.id.btnTestZip).setOnClickListener(v -> testZip());
    }

    void testZip() {
        TextView tvName = findViewById(R.id.tvName);
        TextView tvScoreEnglish = findViewById(R.id.tvScoreEnglish);
        TextView tvScoreMath = findViewById(R.id.tvScoreMath);

        Observable nameTask = Observable.create(new ObservableOnSubscribe<String>() {
            @Override
            public void subscribe(ObservableEmitter<String> emitter) throws Exception {
                 //模擬請求耗時
                Thread.sleep(1000);
                emitter.onNext("小王");
                Log.d(TAG, "Thread:" + Thread.currentThread() + "發送姓名");
            }
        }).subscribeOn(Schedulers.newThread()); //注意將該模擬請求指定到子線程中,模擬并行請求
        Observable scoreTask = Observable.create(new ObservableOnSubscribe<HashMap<String, Integer>>() {
            @Override
            public void subscribe(ObservableEmitter<HashMap<String, Integer>> emitter) throws Exception {
                //模擬請求耗時
                Thread.sleep(5000);
                HashMap map = new HashMap();
                map.put("scoreEnglish", 80);
                map.put("scoreMath", 95);
                Log.d(TAG, "Thread:" + Thread.currentThread() + "發送成績");
                emitter.onNext(map);

            }
        }).subscribeOn(Schedulers.newThread()); //注意將該模擬請求指定到子線程中,模擬并行請求
        Observable.zip(nameTask, scoreTask, new BiFunction<String, HashMap<String, Integer>, Stu>() {
            @Override
            public Stu apply(String s, HashMap<String, Integer> stringIntegerHashMap) throws Exception {
                Log.d(TAG, "Thread:" + Thread.currentThread() + "合并數據");
                return new Stu(s, stringIntegerHashMap.get("scoreMath"), stringIntegerHashMap.get("scoreEnglish"));
            }
        }).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<Stu>() {
            @Override
            public void accept(Stu stu) throws Exception {
                Log.d(TAG, "Thread:" + Thread.currentThread() + "收到最終數據");
                tvName.setText(stu.name);
                tvScoreEnglish.setText(stu.scoreEnglish + "");
                tvScoreMath.setText(stu.scoreMath + "");
            }
        });
    }
}
>>>輸出,觀察“發送姓名”和“發送成績”的時間間隔為4秒,且不再同一線程中執行,說明他們是并行進行的。
14:12:35.455 20450-20516/com.ebm.rxjava D/TestOP: Thread:Thread[RxNewThreadScheduler-2,5,main]發送姓名
14:12:39.456 20450-20517/com.ebm.rxjava D/TestOP: Thread:Thread[RxNewThreadScheduler-3,5,main]發送成績
14:12:39.456 20450-20517/com.ebm.rxjava D/TestOP: Thread:Thread[RxNewThreadScheduler-3,5,main]合并數據
14:12:39.461 20450-20450/com.ebm.rxjava D/TestOP: Thread:Thread[main,5,main]收到最終數據

調度器

RxJava操作符不直接和Threads或者ExecutorServices打交道,而是通過Schedulers(調度器)的通用API來實現對具體并發代碼的抽象。RxJava通過工具類Schedulers提供了幾個標準的調度方式

RxJava operators don't work with Threads or ExecutorServices directly but with so called Schedulers that abstract away sources of concurrency behind a uniform API. RxJava 2 features several standard schedulers accessible via Schedulers utility class.

  • Schedulers.computation(): 在后臺的固定數量的專用線程上運行計算密集型工作。大多數異步操作符使用這個作為默認的調度器

    Run computation intensive work on a fixed number of dedicated threads in the background. Most asynchronous operator use this as their default Scheduler.

  • Schedulers.io(): 在一個動態線程集合上運行類似I/O或阻塞的操作。

    Run I/O-like or blocking operations on a dynamically changing set of threads.

  • Schedulers.single(): 以順序和FIFO方式在單個線程上運行工作。

    Run work on a single thread in a sequential and FIFO manner.

  • Schedulers.trampoline(): ???在一個參與線程中以順序和FIFO方式運行工作,通常用于測試目的。

    Run work in a sequential and FIFO manner in one of the participating threads, usually for testing purposes.

其他

Consumer、Observer、Publisher

Consumer和Observer都是接口,只是Consumer中只有一個accept方法,而Observer中有onNex、onComplete、onError、onSubsribe(Disposable d)四個個方法。其中onSubscribe中的Disposable可以用來作為上下游的開關,調用Disposable.dispose()可以停止接收上游發送的事件。

Publisher能夠提供一個無邊際數量的對象序列,當收到訂閱請求時發布這些無數量限制的元素。Flowable繼承自Publisher。

onNext、onComplete、onError

? 這三個方法是Emitter(發射器)接口中定義的三個方法,分別表示事件發送、完成、異常。

  • 上游可以發送無限個onNext, 下游也可以接收無限個onNext.
  • 當上游發送了一個onComplete后, 上游onComplete之后的事件將會繼續發送, 而下游收到onComplete事件之后將不再繼續接收事件.
  • 當上游發送了一個onError后, 上游onError之后的事件將繼續發送, 而下游收到onError事件之后將不再繼續接收事件.
  • 上游可以不發送onComplete或onError.
  • 最為關鍵的是onComplete和onError必須唯一并且互斥, 即不能發多個onComplete, 也不能發多個onError, 也不能先發一個onComplete, 然后再發一個onError, 反之亦然
  • 注: 關于onComplete和onError唯一并且互斥這一點, 是需要自行在代碼中進行控制, 如果你的代碼邏輯中違背了這個規則, **并不一定會導致程序崩潰. ** 比如發送多個onComplete是可以正常運行的, 依然是收到第一個onComplete就不再接收了, 但若是發送多個onError, 則收到第二個onError事件會導致程序會崩潰。
并行處理

在RxJava中并行處理,體現在在不同的獨立的flow中進行計算的結果,最終被合并到一個flow中去。以flowMap為例:

Flowable.range(1, 10)
  .flatMap(v -> Flowable.just(v).subscribeOn(Schedulers.computation()).map(w -> w * w) )
  .blockingSubscribe(v -> Log.d(TAG, String.valueOf(v)));
>>>輸出:
05:58:40.317 D/TestOP: 1
05:58:40.317 D/TestOP: 9
05:58:40.317 D/TestOP: 25
05:58:40.317 D/TestOP: 49
05:58:40.318 D/TestOP: 81
05:58:40.318 D/TestOP: 4
05:58:40.318 D/TestOP: 16
05:58:40.320 D/TestOP: 36
05:58:40.321 D/TestOP: 64
05:58:40.321 D/TestOP: 100
背壓

當上下游在不同的線程中,發射和接收數據的速率可能有所不同,為了避免超出預期的內存開銷,或者為了滿足跳過、舍棄某些數據的需求,所謂的背壓就產生了。背壓是一種不同操作能夠表達他們能處理多少數據的流控制機制。背壓用來在不知道上游將會發送多少數據量的時候,用來限制內存開銷的。RxJava的Flowable是專門用來處理背壓的。

Flowable.create(new FlowableOnSubscribe<String>(){
  @Override
  public void subscribe(FlowableEmitter<String> emitter) throws Exception {
    for(int i=0;i<1000;i++) {
      emitter.onNext(i + "");
    }
  }
}, BackpressureStrategy.ERROR).subscribeOn(Schedulers.newThread())
  .observeOn(Schedulers.newThread())
  .subscribe(new Consumer<String>() {
    @Override
    public void accept(String s) throws Exception {
      Thread.sleep(1000);
      Log.e(TAG,"處理數據" + s);
    }
  });

Flowable將數據對象發送到一個大小為128的緩沖區內,等待下游線程從該緩存區中取出數據對象進行處理后,等緩沖區有空閑位置時,再將數據對象放置到緩沖區內。如果此時,下游對象沒有使用request(long n)從緩存區拉取數據,那么此時有幾種不同的背壓策略(BackpressureStrategy可供選擇):

/**
 * Represents the options for applying backpressure to a source sequence.
 */
public enum BackpressureStrategy {
    /**
     * OnNext events are written without any buffering or dropping.
     * Downstream has to deal with any overflow.
     * <p>Useful when one applies one of the custom-parameter onBackpressureXXX operators.
     */
    MISSING,
    /**
     * Signals a MissingBackpressureException in case the downstream can't keep up.
     */
    ERROR,
    /**
     * Buffers <em>all</em> onNext values until the downstream consumes it.
     */
    BUFFER,
    /**
     * Drops the most recent onNext value if the downstream can't keep up.
     */
    DROP,
    /**
     * Keeps only the latest onNext value, overwriting any previous value if the
     * downstream can't keep up.
     */
    LATEST
}
RxJavaPlugins

API文檔定義:Utility class to inject handlers to certain standard RxJava operations.

簡單來說,RxJavaPlugins提供了一種讓我們通過hook的方式自定義RxJava行為的途徑。首先查看該類的源碼,可以看到所有的方法對象都是static的,我們通過如下方式設置RxJavaPlugins的靜態成員變量:

RxJavaPlugins.setOnMaybeSubscribe(new BiFunction<Maybe, MaybeObserver, MaybeObserver>() {
  @Override
  public MaybeObserver apply(Maybe maybe, MaybeObserver maybeObserver) throws Exception {
    return new MyCustomMaybeObserver(maybeObserver); //這個maybeObserver就是我們真正的下游
  }
});

再來看一段Maybe的最終subscribe的源碼:

//Maybe.subscribe(MaybeObserver<? super T> observer)
public final void subscribe(MaybeObserver<? super T> observer) {
  ObjectHelper.requireNonNull(observer, "observer is null");
  observer = RxJavaPlugins.onSubscribe(this, observer);
  ObjectHelper.requireNonNull(observer, "The RxJavaPlugins.onSubscribe hook returned a null MaybeObserver. Please check the handler provided to RxJavaPlugins.setOnMaybeSubscribe for invalid null returns. Further reading: https://github.com/ReactiveX/RxJava/wiki/Plugins");

  try {
    subscribeActual(observer);
  } catch (NullPointerException ex) {
    throw ex;
  } catch (Throwable ex) {
    Exceptions.throwIfFatal(ex);
    NullPointerException npe = new NullPointerException("subscribeActual failed");
    npe.initCause(ex);
    throw npe;
  }
}

從上面可以看出,subscriberActural(observer)中的observer是經過RxJavaPlugins進行處理后得到的observer,再看RxJavaPlugins.onSubscribe(this,observer)代碼:

//RxJavaPlugins.onSubscribe(@NonNull Maybe<T> source, @NonNull MaybeObserver<? super T> observer)
public static <T> MaybeObserver<? super T> onSubscribe(@NonNull Maybe<T> source, @NonNull MaybeObserver<? super T> observer) {
  BiFunction<? super Maybe, ? super MaybeObserver, ? extends MaybeObserver> f = onMaybeSubscribe; //這個onMaybeSubscribe就是前面說的RxJavaPlugins的靜態變量
  if (f != null) {
    return apply(f, source, observer);
  }
  return observer;
}

所以,可以把RxJavaPlugins理解為一個存儲自定義observe行為的地方,在對Observerable、Maybe、Single等進行observe時,會判斷RxJavaPlugins中是否定義過自定義的Observer,如果有就進行apply操作之后返回新的Observer,否則原樣返回。

本段參考:給初學者的RxJava2.0教程(十)

RxJava與RxAndroid
  • RxAndroid是RxJava的一個針對Android平臺的擴展,主要用于 Android 開發。
  • RxAndroid只是在RxJava的基礎上添加了少數的幾個類,目的是能夠在Android中更簡單的使用RxJava,特別提供了可以指定到Android主線程的Scheduler,除此之外還增加了基于RxJavaPlugins的擴展RxAndroidPlugins。

操作符

操作符分為創建、轉換、延時、條件、過濾這幾大類。

轉換操作符
map

將輸入對象經過Function<SourceType,TargetType>從SourceType轉換為TargetType

Observable.fromArray(new String[]{"A","B","C"})
    .map(new Function<String, String>() {
      @Override
      public String apply(String s) throws Exception {
        return s + "_" + s;
      }
      }).subscribe(new Consumer<String>() {
     @Override
     public void accept(String s) throws Exception {
       System.out.println(s);
     }
});
>>>輸出:
A_A
B_B
C_C
flatMap

和map類似對流過的數據做轉換,只是flatMap會將數據轉換成ObservableSource類型的數據對象,被轉換的流被分散到多個并行執行的ObservableSource中,所以不能保證執行的順序

Flowable.range(1, 10)
    .flatMap(v -> Flowable.just(v).subscribeOn(Schedulers.computation()).map(w -> w * w) )
    .subscribe(new Consumer<Integer>() {
        @Override
        public void accept(Integer integer) throws Exception {
            Log.d(TAG, String.valueOf(integer));
        }
    });
>>>輸出:
D/TestOP: 1
D/TestOP: 9
D/TestOP: 4
D/TestOP: 16
D/TestOP: 49
D/TestOP: 64
D/TestOP: 81
D/TestOP: 100
D/TestOP: 25
D/TestOP: 36
concatMap
Flowable.range(1, 10)
    .concatMap(v -> Flowable.just(v).subscribeOn(Schedulers.computation()).map(w -> w * w) )
    .subscribe(new Consumer<Integer>() {
        @Override
        public void accept(Integer integer) throws Exception {
            Log.d(TAG, String.valueOf(integer));
        }
    });
D/TestOP: 1
D/TestOP: 4
D/TestOP: 9
D/TestOP: 16
D/TestOP: 25
D/TestOP: 36
D/TestOP: 49
D/TestOP: 64
D/TestOP: 81
D/TestOP: 100
zip

合并多個事件流(Observable),生成一個新的事件流(Observable)。最終合并的事件流的事件數有前面被合并的事件流中事件個數最少的決定。舉一個請求兩個耗時數據的例子:

void testZip() {
        TextView tvName = findViewById(R.id.tvName);
        TextView tvScoreEnglish = findViewById(R.id.tvScoreEnglish);
        TextView tvScoreMath = findViewById(R.id.tvScoreMath);

        Observable nameTask = Observable.create((ObservableOnSubscribe<String>)
                emitter -> {
            Thread.sleep(1000); //模擬耗時
            emitter.onNext("小王");
            Log.d(TAG, "Thread:" + Thread.currentThread() + "發送姓名");
        }).subscribeOn(Schedulers.newThread());
        Observable scoreTask = Observable.create((ObservableOnSubscribe<HashMap<String, Integer>>)
                emitter -> {
            Thread.sleep(5000); //模擬耗時
            HashMap map = new HashMap();
            map.put("scoreEnglish", 80);
            map.put("scoreMath", 95);
            Log.d(TAG, "Thread:" + Thread.currentThread() + "發送成績");
            emitter.onNext(map);
        }).subscribeOn(Schedulers.newThread());
        Observable.zip(nameTask, scoreTask, (BiFunction<String, HashMap<String, Integer>, Stu>)
                (s, stringIntegerHashMap) -> {
            Log.d(TAG, "Thread:" + Thread.currentThread() + "合并數據");
            return new Stu(s, stringIntegerHashMap.get("scoreMath"), stringIntegerHashMap.get("scoreEnglish"));
        }).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread())
                .subscribe((Consumer<Stu>) stu -> {
            Log.d(TAG, "Thread:" + Thread.currentThread() + "收到最終數據");
            tvName.setText(stu.name);
            tvScoreEnglish.setText(stu.scoreEnglish + "");
            tvScoreMath.setText(stu.scoreMath + "");
        });
    }
merge

將2個并行進行的事件流合并到一個事件流中,按時間順序執行。

void testMerge() {
    Observable observable1 = Observable.interval(1000l,TimeUnit.MILLISECONDS).take(3);
    Observable observable2 = Observable.interval(2000l,TimeUnit.MILLISECONDS).take(3);
    Observable.merge(observable1,observable2).subscribe(new Consumer<Long>() {
        @Override
        public void accept(Long i) throws Exception {
            Log.d(TAG,i.toString());
        }
    });
}
//輸出:
17:42:42.555 D/TestOP: 0
17:42:43.554 D/TestOP: 1
17:42:43.555 D/TestOP: 0
17:42:44.555 D/TestOP: 2
17:42:45.555 D/TestOP: 1
17:42:47.555 D/TestOP: 2  
concat
Observable observable1 = Observable.interval(1000l,TimeUnit.MILLISECONDS).take(3);
Observable observable2 = Observable.interval(2000l,TimeUnit.MILLISECONDS).take(3);
Observable.concat(observable1,observable2).subscribe(new Consumer<Long>() {
    @Override
    public void accept(Long i) throws Exception {
        Log.d(TAG,i.toString());
    }
});

>>>輸出:
17:46:18.587 D/TestOP: 0
17:46:19.587 D/TestOP: 1
17:46:20.587 D/TestOP: 2
17:46:22.588 D/TestOP: 0
17:46:24.588 D/TestOP: 1
17:46:26.588 D/TestOP: 2
延時操作符
delay

使事件流經過一段時間延遲后再開始執行

void testDelay() {
  Log.d(TAG,"TestDelay");
  Observable.fromArray(new String[]{"A","B","C"})
    .delay(2000, TimeUnit.MILLISECONDS)
    .subscribe(s -> Log.d(TAG,s));
}
>>>輸出:
05:53:34.615 D/TestOP: TestDelay
05:53:36.643 D/TestOP: A
05:53:36.643 D/TestOP: B
05:53:36.643 D/TestOP: C
interval

在經過最初的延時(initialDelay)開始從0L發送數據,每次經過一個時間間隔(period),發送一個增大的數字。

Returns an Observable that emits a {@code 0L} after the {@code initialDelay} and ever increasing numbers after each {@code period} of time thereafter, on a specified {@link Scheduler}.

Observable.interval(1000l,TimeUnit.MILLISECONDS).subscribe(new Consumer<Long>() {
  @Override
  public void accept(Long aLong) throws Exception {
    Log.d(TAG, String.valueOf(aLong));
  }
});
>>>輸出:
0
1
2
4
5
6
...
skip

跳過事件序列的前n項,以interval的代碼為基礎,跳過前面10項

Observable.interval(1000l,TimeUnit.MILLISECONDS).skip(10).subscribe(new Consumer<Long>() {
  @Override
  public void accept(Long aLong) throws Exception {
    Log.d(TAG, String.valueOf(aLong));
  }
});
>>>輸出:
10
11
12
13
14
15
...
take

只取事件序列的前N項,以skip的代碼為基礎,跳過前10項后,取5項

Observable.interval(1000l,TimeUnit.MILLISECONDS).skip(10).take(5)subscribe(new Consumer<Long>() {
  @Override
  public void accept(Long aLong) throws Exception {
    Log.d(TAG, String.valueOf(aLong));
  }
});
>>>輸出:
10
11
12
13
14
defer

**直到有觀察者(Observer)訂閱時,才動態創建觀察者對象(Observable) **

int i = 1;
void testDefer() {
  Observable<Integer> observable = Observable
    .defer((Callable<ObservableSource<Integer>>) () -> Observable.just(i));
  i = 2;
  observable.subscribe(it -> Log.d(TAG,it.toString()));
}
>>>輸出:2
過濾操作符
filter
Flowable.range(0,10).filter(new Predicate<Integer>() {
    @Override
    public boolean test(Integer integer) throws Exception {
        if(integer.intValue() % 2 == 0) {
            return true;
        }
        return false;
    }
}).subscribe(new Consumer<Integer>() {
    @Override
    public void accept(Integer integer) throws Exception {
        Log.d(TAG,integer.toString());
    }
});
>>>輸出:
0
2
4
6
8
組合使用
定時發送自定義事件
//可以使用from+interval+zip實現
/**
     * 測試固定時間間隔發送自定義消息
     */
void testIntervalCustomMsg() {
    Observable.zip(Observable.fromArray(new String[]{"A","B","C"})
                   ,Observable.interval(3000l,TimeUnit.MILLISECONDS)
                   ,new BiFunction<String,Long,String>(){
                       @Override
                       public String apply(String s, Long aLong) throws Exception {
                           return s;
                       }
                   }).subscribe(new Consumer<String>() {
        @Override
        public void accept(String o) throws Exception {
            Log.d(TAG,o.toString());
        }
    });
}

最后,來一張別人整理的操作符大全圖片(原地址:http://www.lxweimin.com/p/12883e1b59f9)

avatar

參考:

Rxjava操作符大全

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,196評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,456評論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,955評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,195評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,725評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,608評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,802評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,048評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,424評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,762評論 2 372

推薦閱讀更多精彩內容