這一篇文章開始,我們要接觸更複雜的一些知識點
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()有一個特點就是一對一的轉換。比如
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyNyYTOwkDMwEzNwkDM2EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
它将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,而這一切都神不知鬼不覺的,所謂移花接木。
貼一張我自己畫的圖
這是我自己的了解,為了防止了解不到位。貼一張官網的圖檔。
再總結一下,因為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運用到正常的開發當中去了。
下面的内容,我會更深入。
未完待續。。。。。