这一篇文章开始,我们要接触更复杂的一些知识点
翻译过来就是操作符的意思。什么用途呢? 先看场景分析。 一般在学校的学生花名册找一个学生的话,是通过学生的学号。然后通过学号检索出学生的详细情况。现在我们就要做这样的工作,通过学号找出学生,然后在屏幕上显示学生名字。
我们先定义一个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(6000);
} 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(123456777));
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关键字。
Observable.create(new Observable.OnSubscribe<Student>() {
@Override
public void call(Subscriber<? super Student> subscriber) {
subscriber.onNext(getStudentInfo(123456777));
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的意义所在。
如果用户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()这个概念的时候都显得很谨慎,怕自己讲不清楚。我在这里也尽量用很通俗直白的方式进行讲解。
我们已经见识过了map()的美妙之处,但map()有一个特点就是一对一的转换。比如
它将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这个应用场景很绕。 在例子中,项目经理是一个数组,通过项目经理可以得到项目工程的列表,而项目工程列表中的各个项目可以得到项目成员的名字。
这个比较实用,直接张贴官网的说明文档
emit only the first n items emitted by an Observable
只发射指定个数的数据项。
Observable.just(1, 2, 3, 4, 5, 6, 7, 8)
.take(4)
.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.
这个也非常实用。可以过滤我们不需要处理的数据项,阻止它们的发射。在这里也直接贴官网文档。
emit only those items from an Observable that pass a predicate test
示例代码:
Observable.just(1, 2, 3, 4, 5)
.filter(new Func1<Integer, Boolean>() {
@Override
public Boolean call(Integer item) {
return( item < 4 );
}
}).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
Sequence complete.
因为在filter()中进行了处理,所以小于4的数据才正常发射被subscriber观察到。
实际上RxAndroid,还有很多的Operator,甚至可以说是巨量的,但因为时间我有限,我只挑选了我觉得实用的常见的内容。大家可以去官网多去了解下。
到目前为至,我们学习的内容能够让自己将RxAndroid运用到正常的开发当中去了。
下面的内容,我会更深入。
未完待续。。。。。