[譯] 開發者(也就是我)與Rx Observable 類的對話 [ Android RxJava2 ] ( 這到底是什麼?) 第五部分

龍騎將楊影楓發表於2017-05-04

開發者(也就是我)與Rx Observable 類的對話 [ Android RxJava2 ] ( 這到底是什麼?) 第五部分

又是新的一天,是時候學點新東來西來讓今天變得酷炫了。

大家好,希望你們都過的不錯。這是我們 RxJava2 Android 系列的第五篇文章 [ part1, part2, part3, part4 ] 。在這篇文章中,我們會繼續研究 Rx Java Android 。

動機

動機和我在第一部分 part1 中分享給大家的一樣。現在我們把之前 4 篇學到的東西融會貫通起來。

介紹:

當我在學習 Rx java Android 的某一天,我有幸與一位 Rx Java 的 Observable 類進行了親切友好的交談。好訊息是 Observable 類很厚道,令我驚歎不已。我一直以為 Rx Java 是個大坑逼。他/她不想和開發者做朋友,總給他們穿小鞋。
但是在和 Observable 類談話以後,我驚喜的發現我的觀點是錯的。

我:你好,Observable 類,吃了嘛您?

Observable 類:你好 Hafiz Waleed Hussain ,我吃過啦。

我:為啥你的學習曲線這麼陡峭?為啥你故意刁難開發者?你這麼搞要沒朋友了。

Observable 類:哈哈,你說的是。我真想交很多朋友,不過我現在也有一些好哥們兒。他們在不同的論壇上討論我,介紹我和我的能力。而且這些傢伙真的很棒,他們花了很久的時間和我呆在一起。只有精誠所至,才會金石為開。但問題是,很多想撩我的人只走腎不走心。他們關注我了一小會就去刷推特臉書,把我給忘了。所以說,對我不真誠的人又如何指望我和他們交朋友呢?

我:好吧,如果想和你交朋友的話,我該怎麼做?

Observable 類:把注意力集中在我身上,並且堅持足夠長的時間,然後你就知道我有多真誠了。

我:嗯,實話實說我不擅長集中精神,但是我擅長無視周圍。這樣可以嘛?

Observable 類:當然,只要你和我在一起的時候可以心無旁騖,我會是你的好朋友的。

我:哇哦,我有種預感,我會和你交上朋友的。

Observable 類:當然,任何人都可以把我當好朋友。

我:現在我有些問題,可以問了嘛?

Observable 類:當然,你可以問成千上萬個問題。我會給你答案,但是重要的是需要你自己花時間去思考和吸收。

我:我會的。如果我想把資料轉化為 Observable 物件,在 Rx Java 2 Android 裡怎麼實現?

Observable 類:這個問題的答案很長很長。如果你來看我(Rx Java 2 Observable 類)的原始碼,你就會發現我一共有12904行程式碼。(校對 Phoenix 注:在 RxJava 2.0.9 版本。Observable 類已經成功增肥到 13728 行。)

[譯] 開發者(也就是我)與Rx Observable 類的對話 [ Android RxJava2 ] ( 這到底是什麼?) 第五部分

我的團隊裡也有好幾個朋友,可以根據開發者的需求返回 Observable 物件,比如 map ,filter。不過現在我會告訴你幾個可以幫助你把任何東西轉化為 Observable 物件的方法。抱歉我的回答可能會很長,但是也不會很無聊。我不僅僅會演示這些方法如何建立 Observable 物件,同時也會向你展示如何對手頭邊程式碼進行重構。

1 just()

通過這個方法,你可以把任意(多個)物件轉化成以此物件為泛型的 Observable 物件( Observable )。

String data= "Hello World";
    Observable.just(data).subscribe(s -> System.out.println(s));
Output:
    Hello World複製程式碼

如果你的資料不止一個,可以像下面那樣呼叫 just 方法 :

String data= "Hello World";
Integer i= 4500;
Boolean b= true;
    Observable.just(data,i,b).subscribe(s -> System.out.println(s));
Output:
    Hello World
    4500
    true複製程式碼

此 API 最多可接收 10 個資料做引數。

    Observable.just(1,2,3,4,5,6,7,8,9,10).subscribe(s -> System.out.print(s+" "));
Output:
    1 2 3 4 5 6 7 8 9 10複製程式碼
[譯] 開發者(也就是我)與Rx Observable 類的對話 [ Android RxJava2 ] ( 這到底是什麼?) 第五部分

樣例程式碼:(不是個好例子,只是給點提示,告訴你如何在自己的程式碼中使用)

    public static void main(String[] args) {
String username= "username";
String password= "password";
        System.out.println(validate(username, password));
    }

    private static boolean validate(String username, String password) {
boolean isUsernameValid= username!=null && !username.isEmpty() && username.length() > 3;
boolean isPassword= password!=null && !password.isEmpty() && password.length() > 3;
    return isUsernameValid && isPassword;
}複製程式碼

使用 Observable 類進行重構:

private static boolean isValid= true;
private static boolean validate(String username, String password) {
    Observable.just(username, password).subscribe(s -> {
if (!(s != null && !s.isEmpty() && s.length() > 3))
           throw new RuntimeException();
}, throwable -> isValid= false);
    return isValid;
}複製程式碼

2 from…

我有一大堆的 API 可以把複雜的資料結構轉化為 Observable 物件,比如下面那些以關鍵字 from 開頭的方法:

[譯] 開發者(也就是我)與Rx Observable 類的對話 [ Android RxJava2 ] ( 這到底是什麼?) 第五部分

我想這些 API 從名字就可以看懂它們的意思,所以也不需要更多解釋了。不過我會給你一些例子,這樣你可以在自己的程式碼裡用的更舒服。

(校對 Phoenix 注:
雖然 fromCallable, fromPublisher, fromFuture 也是 from 開頭的方法。但是他們互相之間區別很大。尤其是 fromCallable 和 fromPublisher。)

public static void main(String[] args) {

List<Tasks> tasks= Arrays.asList(new Tasks(1,"description"),
            new Tasks(2,"description"),new Tasks(4,"description"),
            new Tasks(3,"description"),new Tasks(5,"description"));
    Observable.fromIterable(tasks)
            .forEach(task -> System.out.println(task.toString()));
}

private static class Tasks {
    int id;String description;
public Tasks(int id, String description) {this.id= id;this.description = description;}
    @Override
public String toString() {return "Tasks{" + "id=" + id + ", description='" + description + '\'' + '}';}
}
}複製程式碼

從陣列轉化為 Observable 物件

    public static void main(String[] args) {
Integer[] values= {1,2,3,4,5};
        Observable.fromArray(values)
                .subscribe(v-> System.out.print(v+" "));
    }複製程式碼

兩個例子就夠啦,回頭你可以親自試試其他的。

3 create()

你可以把任何東西強行轉為 Observable 物件。這個 API 過於強大,所以個人建議使用這個API之前,應該先找找有沒有其他的解決方式。大約99%的情況下,你可以用其他的 API 來解決問題。但如果實在找不到,那麼就用它也可以。

(校對 Phoenix 注:這裡可能作者對 RxJava 2 的 create 還停留在 RxJava 1 的階段。 RxJava 1.x 確實不推薦 create 方法。而 RxJava 2 的 create 方法是推薦方法。並不是 99% 的情況都可以被取代。 RxJava 1.x 的 create 方法現已經成為 RxJava 2.x 的 unsafeCreate ,RxJava 1.2.9 版本也加入了新的安全的 create 過載方法。)

    public static void main(String[] args) {
final int a= 3, b = 5, c = 9;
Observable me= Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> observableEmitter) throws Exception {
                observableEmitter.onNext(a);
                observableEmitter.onNext(b);
                observableEmitter.onNext(c);
                observableEmitter.onComplete();
            }
        });
        me.subscribe(i-> System.out.println(i));
    }複製程式碼

4 range()

這就像是一個 for 迴圈,就像下面的程式碼顯示的那樣。

    public static void main(String[] args) {
        Observable.range(1,10)
                .subscribe(i-> System.out.print(i+" "));
    }
Output:
    1 2 3 4 5 6 7 8 9 10複製程式碼

再來一個例子:

public static void main(String[] args) {

List<String> names= Arrays.asList("Hafiz", "Waleed", "Hussain", "Steve");
for (int i= 0; i < names.size(); i++) {
if(i%2== 0)continue;
        System.out.println(names.get(i));
    }

    Observable.range(0, names.size())
.filter(index->index%2==1)
            .subscribe(index -> System.out.println(names.get(index)));
}複製程式碼

5 interval()

這個 API 碉堡了。我用兩種方法實現同一種需求,你可以比較一下。第一種我用 Java 的執行緒來實現,另一種我用 interval() 這個 API ,兩種方法會得到同一個結果。

(校對 Phoenix 注:interval() 會預設在 Scheduler.computation() 進行操作。)

public static void main(String[] args) {
    new Thread(() -> {
        try {
            sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        greeting();
    }).start();

    Observable.interval(0,1000, TimeUnit.MILLISECONDS)
            .subscribe(aLong -> greeting());
}

public static void greeting(){
    System.out.println("Hello");
}複製程式碼

6 timer()

又是一個好的 API。在程式中如果我想一秒鐘後呼叫什麼方法,可以用 timer ,就像下面展示的那樣:

public static void main(String[] args) throws InterruptedException {
    Observable.timer(1, TimeUnit.SECONDS)
            .subscribe(aLong -> greeting());
    Thread.sleep(2000);
}

public static void greeting(){
    System.out.println("Hello");
}複製程式碼

7 empty()

這個 API 很有用,尤其是在有假資料的時候。這個 API 建立的 Observable 物件中,註冊的 Observer 物件只呼叫 complete 方法。比如這個例子,如果在測試執行時傳送給我假資料,在生產環境下就呼叫真的資料。

public static void main(String[] args) throws InterruptedException {
    hey(false).subscribe(o -> System.out.println(o));
}

private static Observable hey(boolean isMock) {
return isMock ? Observable.empty(): Observable.just(1, 2, 3, 4);
}複製程式碼

8 defer()

這個 API 在很多情況下都會很有用。我來用下面的例子解釋一下:

public static void main(String[] args) throws InterruptedException {
Employee employee= new Employee();
employee.name= "Hafiz";
employee.age= 27;
Observable observable= employee.getObservable();
employee.age= 28;
    observable.subscribe(s-> System.out.println(s));
}

private static class Employee{
    String name;
    int age;
    Observable getObservable(){
        return Observable.just(name, age);
    }
}複製程式碼

上面的程式碼會輸出什麼呢?如果你的答案是 age = 28 那就大錯特錯了。基本上所有建立 Observable 物件的方法在建立時就記錄了可用的值。就像剛才的資料實際上輸出的是 age = 27 , 因為在我建立 Observable 的時候 age 值是 27 ,當我把 age 的值變成 28 的時候 Observable 類已經建立過了。所以怎麼解決這個問題呢?是的,這個時候就輪到 defer 這個 API 出場了。太有用了!當你使用 defer 以後,只有註冊(subscribe)的時候才建立 Observable 類。用這個 API ,我就可以獲得想要的值。

Observable getObservable(){
  //return Observable.just(name, age);
  return Observable.defer(()-> Observable.just(name, age));
}複製程式碼

這樣我們的 age 的輸出值就是 28 了。

(校對 Phoenix 注:Observable 的建立方法中,並不是像原文中寫到的,“基本上所有建立 Observable 的方法在建立時就記錄了可用的值”。而是隻有 just, from 方法。 create , fromCallable 等等方法都是在 subscribe 後才會呼叫。文中的例子可以使用 fromCallable 代替 defer。)

9 error()

一個可以彈出錯誤提示的方法。當我們討論 Observer 類和他的方法的時候,我再和你分享吧。

10 never()

這個 API 建立出的 Observable 物件沒有包含泛型。

(譯者注:Observable.never 雖然可以得到一個 Observable 物件,但是註冊的對應 Observer 既不會呼叫 onNext 方法也不會 onCompleted 方法,甚至不會呼叫 onError 方法)

我:哇哦。謝謝你,Observable 類。謝謝你耐心又詳細的回答,我會把你的回答記在我的祕籍手冊上的。話說,你可以把函式也轉化成 Observable 物件嗎?

Observable 類:當然,注意下面的程式碼。

 public static void main(String[] args) throws InterruptedException {
    System.out.println(scale(10,4));
    Observable.just(scale(10,4))
            .subscribe(value-> System.out.println(value));
}

private static float scale(int width, int height){
    return width/height*.3f;
}複製程式碼

我:哇哦,你真的好強大。現在我想問你有關操作符,比如 map ,filter 方面的問題。但是有關 Observable 物件建立,如果還有什麼我因為缺乏知識沒問到的地方,再多告訴我一點唄。

Observable 類:其實還有很多。我在這裡介紹兩類 Observable 物件。一種叫做 Cold Observable,第二個是 Hot Observable。在...

總結:

大家好。這篇對話已經非常非常的長,我需要就此擱筆了。不然這篇文章就會像大部頭的書,可能看上去不錯,但是主要目的就跑偏了。我希望,我們可以循序漸進的學習。所以我要暫停我的對話,然後在下一篇繼續。讀者可以試試親自實現這些方法,如果可能的話在實際的專案中去運用、重構。最後我想說,謝謝 Observable 類給我了這麼多他/她的時間。

週末愉快,再見~


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOSReact前端後端產品設計 等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃

相關文章