天天看點

RxAndroid從零開始學之四(常見操作符)

這一篇文章開始,我們要接觸更複雜的一些知識點

Operator

翻譯過來就是操作符的意思。什麼用途呢?

先看場景分析。

一般在學校的學生花名冊找一個學生的話,是通過學生的學号。然後通過學号檢索出學生的詳細情況。現在我們就要做這樣的工作,通過學号找出學生,然後在螢幕上顯示學生名字。

我們先定義一個Student.java類

public class Student {

    private long id;

    private String name;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
           

然後通過學号找學生,假設有這麼一個方法getStudentInfo(long id);

public Student getStudentInfo(long id){
        Student student = new Student();
        student.setId(id);
        student.setName("test");
        try {
            Thread.sleep();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return student;
    }
           

假設getStudentInfo()比較耗時,我們選擇用RxAndroid方式編寫代碼。

Observable.create(new Observable.OnSubscribe<Student>() {
     @Override
     public void call(Subscriber<? super Student> subscriber) {
         subscriber.onNext(getStudentInfo());
         subscriber.onCompleted();
     }
 }).subscribeOn(Schedulers.io())
 .observeOn(AndroidSchedulers.mainThread())
 .subscribe(new Action1<Student>() {
     @Override
     public void call(Student s) {
         mTvMsg.setText(s.getName());
     }
 });
           

這樣的代碼沒有什麼問題。我這裡隻抛出一個點,因為mTvMsg這個TextView隻關心的是一個String對象,上面的例子直接抛出一個Student對象與相應的Subscriber相關聯。有沒有更進一步呢,讓Subscriber隻接收String對象,其他的資訊不需要關心。

答案是有的,我們可以通過map關鍵字。

map()

Observable.create(new Observable.OnSubscribe<Student>() {
        @Override
        public void call(Subscriber<? super Student> subscriber) {
            subscriber.onNext(getStudentInfo());
            subscriber.onCompleted();
        }
    }).map(new Func1<Student, String>() {

        @Override
        public String call(Student student) {
            return student.getName();
        }
    })
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Action1<String>() {
        @Override
        public void call(String s) {
            mTvMsg.setText(s);
        }
    });
           

這段代碼和上面的代碼是不一樣的。

也許你會覺得這很多餘,但是這真的是一個極棒的檢驗。

試想一下,Observable對象是開發員A編寫,Subscriber對象是開發員B編寫,Observable現有的代碼隻能向外提供一個Student對象,而Subscriber隻關心String類型的變量name,那好,隻需要通過Student.getName()代碼進行轉換,要麼讓A重新寫,要麼讓B重新寫。但如果我非得自己寫呢?不想讓别人太累。這正是map的意義所在。

map()更進一步

如果使用者A提供一個原始密碼字元串,而使用者C隻關心一個加密後的字元串,我們可以在中間做一些變換。

Observable<String> pwd = Observable.just("我是密碼");

Subscriber<String>  client = new Subscriber<String>() {
    ......        
   @Override
    public void onNext(String s) {
        mTvMsg.setText("密碼是:"+s);
    }
};

Utils.encrypt(pwd,client);
           

上面的代碼pwd提供原始的密碼,而client隻關心加密後的字元串,并顯示在界面上。而Utils.encrypt()方法是我編寫的一個靜态的工具方法,用來進行字元加密。

Utils.java

public class Utils {

    public static void encrypt(Observable<String> pwd, Subscriber<String> result){

        pwd.map(new Func1<String, String>() {
            @Override
            public String call(String s) {
            return "我是牛逼的加密算法"+s;
            }
        }).subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(result);
    }
}
           

到了這裡是不是感覺到爽了?

也許我還沒有讓你更明白,但記住,map()可以讓你在最初的Observable和最終的Subscriber之間做任何轉換,任何轉換,任何轉換。

flatmap()

很多教程在講flatmap()這個概念的時候都顯得很謹慎,怕自己講不清楚。我在這裡也盡量用很通俗直白的方式進行講解。

我們已經見識過了map()的美妙之處,但map()有一個特點就是一對一的轉換。比如

RxAndroid從零開始學之四(常見操作符)

它将Observable傳遞過來的資料進行加工,然後再傳遞給Subscriber。比如String轉換成String或者String轉換成int類型等等。

但有一種情況是Observable傳遞過來的是一個數組,或者是集合。數組或者集合是沒有辦法直接轉換成單一的類型對象的。這時候顯示map()已經不太适應,而RxAndroid也提供了這種情況的解決方案。那就是flatmap().

舉個例子:

假設軟體開發公司A,有人數多于10名的項目經理,但是有同時進行的超過30個的項目,是以每個項目經理要同時負責不至1個軟體項目,現在要列印出每個項目經理手下的開發人員的名字?怎麼實作呢?先看看map的實作方式

String[] pm = new String[]{"PM1","PM2","PM3"};

Observable.from(pm)
 .map(new Func1<String, List<String>>() {
     @Override
     public List<String> call(String s) {
         //通過項目經理名字得到他負責的項目清單
         List<String> projs = getProject(s);
         return projs;
     }
 }).subscribe(new Action1<List<String>>() {
     @Override
     public void call(List<String> projs) {

         Observable.from(projs)
                 .map(new Func1<String, List<String>>() {
                     @Override
                     public List<String> call(String proj) {
                         //通過項目名稱得到項目組成員的名字
                         List<String> names = getPersonNames(proj);
                         return names;
                     }
                 }).subscribe(new Action1<List<String>>() {
             @Override
             public void call(List<String> names) {
                 //列印項目組的人員的名字
                 for (String name : names) {
                     Log.d(TAG, "person name:" + name);
                 }
             }
         });


     }
 });
           

可以看見在Subcriber中的call中還做了一層嵌套。

而我不想讓Subscrber多做一些事情,我希望在中間做一些變換,而這些變換對于Subscriber和Observable是透明的。

flatmap()可以解決這個問題。

Observable.from(pm)
        .map(new Func1<String, List<String>>() {
            @Override
            public List<String> call(String s) {
                //通過項目經理名字得到他負責的項目清單
                List<String> projs = getProject(s);
                return projs;
            }
        }).flatMap(new Func1<List<String>, Observable<List<String>>>() {
            @Override
            public Observable<List<String>> call(List<String> projs) {
                List<List<String>> names = null;
                for(String proj:projs){
                    //通過項目名稱得到項目組成員的名字
                    names.add(getPersonNames(proj));
                }
                return  Observable.from(names);
            }
        })
        .subscribe(new Action1<List<String>>() {
            @Override
            public void call(List<String> names) {

                //列印項目組的人員的名字
                for (String name : names) {
                    Log.d(TAG, "person name:" + name);
                }
            }
        });
           

我們在中間的部分運作了flatmap()進行了一此變換,将原來的Observable對象替換了一個新的Observable對象,然後由這個新的Observable對象來對接Subscriber,而這一切都神不知鬼不覺的,所謂移花接木。

貼一張我自己畫的圖

RxAndroid從零開始學之四(常見操作符)

這是我自己的了解,為了防止了解不到位。貼一張官網的圖檔。

RxAndroid從零開始學之四(常見操作符)

再總結一下,因為flatmap這個應用場景很繞。

在例子中,項目經理是一個數組,通過項目經理可以得到項目工程的清單,而項目工程清單中的各個項目可以得到項目成員的名字。

  • 原始的Observable隻能提供項目經理花名冊。
  • 最終的Subscriber隻關心各個項目組的成員的名字。
  • 這中間的部分就是flatmap發揮作用的地方

take()

這個比較實用,直接張貼官網的說明文檔

emit only the first n items emitted by an Observable

隻發射指定個數的資料項。

Observable.just(, , , , , , , )
          .take()
          .subscribe(new Subscriber<Integer>() {
        @Override
        public void onNext(Integer item) {
            System.out.println("Next: " + item);
        }

        @Override
        public void onError(Throwable error) {
            System.err.println("Error: " + error.getMessage());
        }

        @Override
        public void onCompleted() {
            System.out.println("Sequence complete.");
        }
    });
           

運作結果是:

Next: 1

Next: 2

Next: 3

Next: 4

Sequence complete.

filter()

這個也非常實用。可以過濾我們不需要處理的資料項,阻止它們的發射。在這裡也直接貼官網文檔。

emit only those items from an Observable that pass a predicate test

示例代碼:

Observable.just(, , , , )
          .filter(new Func1<Integer, Boolean>() {
              @Override
              public Boolean call(Integer item) {
                return( item <  );
              }
          }).subscribe(new Subscriber<Integer>() {
        @Override
        public void onNext(Integer item) {
            System.out.println("Next: " + item);
        }

        @Override
        public void onError(Throwable error) {
            System.err.println("Error: " + error.getMessage());
        }

        @Override
        public void onCompleted() {
            System.out.println("Sequence complete.");
        }
    });
           

運作結果如下:

Next: 
Next: 
Next: 
Sequence complete.
           

因為在filter()中進行了處理,是以小于4的資料才正常發射被subscriber觀察到。

總結

實際上RxAndroid,還有很多的Operator,甚至可以說是巨量的,但因為時間我有限,我隻挑選了我覺得實用的常見的内容。大家可以去官網多去了解下。

到目前為至,我們學習的内容能夠讓自己将RxAndroid運用到正常的開發當中去了。

下面的内容,我會更深入。

未完待續。。。。。