Android开发学习之路--RxAndroid之简单原理

  学习了RxAndroid,其实也就是RxJava了,但是还是不是非常清楚到底RxAndroid有什么用呢?为什么要使用RxAndroid呢?这篇文章讲得不错,RxJava的原理。但是这里还是把整个过程学习下,这里主要参考文章中的原理,再把这个过程实现了一遍,也算是一知半解了。


一般实现

  首先来个简单的例子,选出一个班级里面,学生成绩最好的,然后保存这个学生的姓名,这里简单实现下Student的类。

package com.jared.emrxandroidstudy;

/**
 * Created by jared on 16/3/13.
 */
public class Student implements Comparable<Student> {
    String name;
    int grade;

    @Override
    public int compareTo(Student student) {
        return Integer.compare(grade, student.grade);
    }
}

  这里通过实现Comparable来实现排序,通过成绩grade。接着实现StuApi。

package com.jared.emrxandroidstudy;

import java.util.List;

/**
 * Created by jared on 16/3/13.
 */
public interface StuApi {
    List<Student> queryStudents(String query);
    String store(Student student);
}

  接着实现我们的业务逻辑StudentHelper。

package com.jared.emrxandroidstudy;

import java.util.Collections;
import java.util.List;

/**
 * Created by jared on 16/3/13.
 */
public class StudentHelper {
    StuApi stuApi;

    public String saveTheBestStudent(String query) {
        List<Student> students = stuApi.queryStudents(query);
        Student best = findBest(students);
        String saveName = stuApi.store(best);
        return saveName;
    }

    private Student findBest(List<Student> students) {
        return Collections.max(students);
    }
}

  这里实现了saveTheBestStudent的方法,查询student,获取学生的链表,然后找到grade最大的,然后保存该学生的名字,返回。
  这里需要经过三个步骤,而且每个步骤都是按顺序来实现的,是阻塞的方式的。
  如果查询student花了很长时间的话,那么程序不应该一直在这里,我们需要的是处理完了告诉我们,而不是一直等着,就拿Android的click事件,当我们按下了button,才会触发onClick回调函数。那么接下去我们来实现下吧。

异步实现

  修改StuApi如下:

package com.jared.emrxandroidstudy;

import java.util.List;

/**
 * Created by jared on 16/3/13.
 */
public interface StuApi {
    interface StudentsCallback {
        void onStudentsListGetOk(List<Student> students);
        void onQueryFailed(Exception e);
    }

    interface StoreCallback {
        void onStudentStored(String name);
        void onStoreFailed(Exception e);
    }

    void queryStudents(String query, StudentsCallback studentsCallback);
    void store(Student student, StoreCallback storeCallback);
}

  这里实现了StudentsCallback回调函数接口,当queryStudent成功的时候会回调onStudentsListGetOk,当失败的时候会调用onQueryFailed。StoreCallback回调函数接口,当store成功的时候回调onStudentStored,当失败的时候回调onStoreFailed。
  接着修改StudentHelper:

package com.jared.emrxandroidstudy;

import java.util.Collections;
import java.util.List;

/**
 * Created by jared on 16/3/13.
 */
public class StudentHelper {

    public interface BestStudentCallback {
        void onBestStudentSaved(String name);
        void onError(Exception e);
    }

    StuApi stuApi;

    public void saveTheBestStudent(String query, BestStudentCallback bestStudentCallback) {
        stuApi.queryStudents(query, new StuApi.StudentsCallback() {
            @Override
            public void onStudentsListGetOk(List<Student> students) {
                Student best = findBest(students);
                stuApi.store(best, new StuApi.StoreCallback() {
                    @Override
                    public void onStudentStored(String name) {
                        bestStudentCallback.onBestStudentSaved(name);
                    }

                    @Override
                    public void onStoreFailed(Exception e) {
                        bestStudentCallback.onError(e);
                    }
                });
            }

            @Override
            public void onQueryFailed(Exception e) {
                bestStudentCallback.onError(e);
            }
        });
    }

    private Student findBest(List<Student> students) {
        return Collections.max(students);
    }
}

  这里实现了BestStudentCallback回调函数接口,当query成功的时候调用store方法,接着当store成功后回调onStudentStored方法,当失败后回调onBestStudentSaved,当失败的时候调用onError。
  回调嵌套着回调,而且错误传递也是问题,分不清哪个错误,代码也失去了最开始阻塞方式的那种简洁优雅。

泛型回调实现

  实现泛型回调Callback:

package com.jared.emrxandroidstudy;

/**
 * Created by jared on 16/3/12.
 */
public interface Callback<T> {
    void onResult(T result);
    void onError(Exception e);
}

  这里实现泛型回调函数Callback,实现两个方法,成功onResult,失败onError,成功传入的参数是泛型的,失败统一的Exception。接着添加StuApiWrapper,实现如下:

package com.jared.emrxandroidstudy;

import java.util.List;

/**
 * Created by jared on 16/3/13.
 */
public class StuApiWrapper {
    StuApi stuApi;

    public void queryStudents(String query, Callback<List<Student>> listCallback) {
        stuApi.queryStudents(query, new StuApi.StudentsCallback() {
            @Override
            public void onStudentsListGetOk(List<Student> students) {
                listCallback.onResult(students);
            }

            @Override
            public void onQueryFailed(Exception e) {
                listCallback.onError(e);
            }
        });
    }

    public void store(Student student, Callback<String> callback) {
        stuApi.store(student, new StuApi.StoreCallback() {
            @Override
            public void onStudentStored(String name) {
                callback.onResult(name);
            }

            @Override
            public void onStoreFailed(Exception e) {
                callback.onError(e);
            }
        });
    }
}

  实现queryStudents和store两个方法,接着修改StudentHelper如下:

package com.jared.emrxandroidstudy;

import java.util.Collections;
import java.util.List;

/**
 * Created by jared on 16/3/13.
 */
public class StudentHelper {

    StuApiWrapper stuApiWrapper;

    public void saveTheBestStudent(String query, Callback<String> bestStuCallback) {
        stuApiWrapper.queryStudents(query, new Callback<List<Student>>() {
            @Override
            public void onResult(List<Student> result) {
                Student student = findBest(result);
                stuApiWrapper.store(student, bestStuCallback);
            }

            @Override
            public void onError(Exception e) {
                bestStuCallback.onError(e);
            }
        });
    }

    private Student findBest(List<Student> students) {
        return Collections.max(students);
    }
}

AsyncJob实现

  这里saveTheBestStudent传入一个回调,当queryStudents成功后,会回调stuApiWrapper.store,然后成功的话,会在传入的bestStuCallback回调onStudentStored。
  显然这样还是没有达到简洁明了,还需要传递回调函数,还是觉得麻烦,这里引入AsyncJob。

package com.jared.emrxandroidstudy;

/**
 * Created by jared on 16/3/12.
 */
public abstract class AsyncJob<T> {
    public abstract void start(Callback<T> callback);
}

  如果在异步操作中返回一些临时对象,我们需要定义一个类出来。这样的一个对象需要包括常见的行为(以回调为单一参数)。
  修改StuApiWrapper如下:

package com.jared.emrxandroidstudy;

import java.util.List;

/**
 * Created by jared on 16/3/13.
 */
public class StuApiWrapper {
    StuApi stuApi;

    public AsyncJob<List<Student>> queryStudents(String query) {
        return new AsyncJob<List<Student>>() {
            @Override
            public void start(Callback<List<Student>> callback) {
                stuApi.queryStudents(query, new StuApi.StudentsCallback() {
                    @Override
                    public void onStudentsListGetOk(List<Student> students) {
                        callback.onResult(students);
                    }

                    @Override
                    public void onQueryFailed(Exception e) {
                        callback.onError(e);
                    }
                });
            }
        };
    }

    public AsyncJob<String> store(Student student) {
        return new AsyncJob<String>() {
            @Override
            public void start(Callback<String> callback) {
                stuApi.store(student, new StuApi.StoreCallback() {
                    @Override
                    public void onStudentStored(String name) {
                        callback.onResult(name);
                    }

                    @Override
                    public void onStoreFailed(Exception e) {
                        callback.onError(e);
                    }
                });
            }
        };
    }
}

  这里泛型抽象类AsyncJob实现了queryStudents和store,返回的数据都是AsyncJob。接着看看StudentHelper怎么来实现:

package com.jared.emrxandroidstudy;

import java.util.Collections;
import java.util.List;

/**
 * Created by jared on 16/3/13.
 */
public class StudentHelper {

    StuApiWrapper stuApiWrapper;

    public AsyncJob<String> saveTheBestStudent(String query) {
        return new AsyncJob<String>() {
            @Override
            public void start(Callback<String> bestStudentcallback) {
                stuApiWrapper.queryStudents(query)
                        .start(new Callback<List<Student>>() {
                            @Override
                            public void onResult(List<Student> result) {
                                Student best = findBest(result);
                                stuApiWrapper.store(best)
                                        .start(new Callback<String>() {
                                            @Override
                                            public void onResult(String result) {
                                                bestStudentcallback.onResult(result);
                                            }

                                            @Override
                                            public void onError(Exception e) {
                                                bestStudentcallback.onError(e);
                                            }
                                        });
                            }

                            @Override
                            public void onError(Exception e) {
                                bestStudentcallback.onError(e);
                            }
                        });
            }
        };
    }

    private Student findBest(List<Student> students) {
        return Collections.max(students);
    }
}

  这里在成功query后回调中找到最好成绩的学生,再存储。失败就调用onError了。这里省去了saveTheBestStudent的callback参数,回调都在AsyncJob的start方法里去实现,传递。
  接着我们可以使用 AsyncJob把我们的方法分解成更小的操作。

package com.jared.emrxandroidstudy;

import java.util.Collections;
import java.util.List;

/**
 * Created by jared on 16/3/13.
 */
public class StudentHelper {

    StuApiWrapper stuApiWrapper;

    public AsyncJob<String> saveTheBestStudent(String query) {
        AsyncJob<List<Student>> studentsAsyncJob = stuApiWrapper.queryStudents(query);
        AsyncJob<Student> bestStudentAsyncJob = new AsyncJob<Student>() {
            @Override
            public void start(Callback<Student> callback) {
                studentsAsyncJob.start(new Callback<List<Student>>() {
                    @Override
                    public void onResult(List<Student> result) {
                        callback.onResult(findBest(result));
                    }

                    @Override
                    public void onError(Exception e) {
                        callback.onError(e);
                    }
                });
            }
        };

        AsyncJob<String> storeNameAsyncJob = new AsyncJob<String>() {
            @Override
            public void start(Callback<String> callback) {
                bestStudentAsyncJob.start(new Callback<Student>() {
                    @Override
                    public void onResult(Student result) {
                        stuApiWrapper.store(result)
                                .start(new Callback<String>() {
                                    @Override
                                    public void onResult(String result) {
                                        callback.onResult(result);
                                    }

                                    @Override
                                    public void onError(Exception e) {
                                        callback.onError(e);
                                    }
                                });
                    }

                    @Override
                    public void onError(Exception e) {
                        callback.onError(e);
                    }
                });
            }
        };
        return storeNameAsyncJob;
    }

    private Student findBest(List<Student> students) {
        return Collections.max(students);
    }
}

  这里使用了AsyncJob来转换我们所要的结果,接着我们实现一个转换方法:

package com.jared.emrxandroidstudy;

/**
 * Created by jared on 16/3/12.
 */
public interface Func<T, R> {
    R call(T t);
}

  接口有两个类型成员,T对应于参数类型而R对应于返回类型。
  接着修改AsyncJob:

package com.jared.emrxandroidstudy;

/**
 * Created by jared on 16/3/12.
 */
public abstract class AsyncJob<T> {
    public abstract void start(Callback<T> callback);

    public <R> AsyncJob<R> map(Func<T, R> func) {
        final AsyncJob<T> source = this;
        return new AsyncJob<R>() {
            @Override
            public void start(Callback<R> callback) {
                source.start(new Callback<T>() {
                    @Override
                    public void onResult(T result) {
                        R mapped = func.call(result);
                        callback.onResult(mapped);
                    }

                    @Override
                    public void onError(Exception e) {
                        callback.onError(e);
                    }
                });
            }
        };
    }

    public <R> AsyncJob<R> flatMap(Func<T, AsyncJob<R>> func) {
        final AsyncJob<T> source = this;
        return new AsyncJob<R>() {
            @Override
            public void start(Callback<R> callback) {
                source.start(new Callback<T>() {
                    @Override
                    public void onResult(T result) {
                        AsyncJob<R> mapped = func.call(result);
                        mapped.start(new Callback<R>() {
                            @Override
                            public void onResult(R result) {
                                callback.onResult(result);
                            }

                            @Override
                            public void onError(Exception e) {
                                callback.onError(e);
                            }
                        });
                    }

                    @Override
                    public void onError(Exception e) {
                        callback.onError(e);
                    }
                });
            }
        };
    }
}

  这里实现了map和flatMap两个方法,接着修改StudentHelper如下:

package com.jared.emrxandroidstudy;

import java.util.Collections;
import java.util.List;

/**
 * Created by jared on 16/3/13.
 */
public class StudentHelper {

    StuApiWrapper stuApiWrapper;

    public AsyncJob<String> saveTheBestStudent(String query) {
        AsyncJob<List<Student>> bestListAsyncJob = stuApiWrapper.queryStudents(query);
        AsyncJob<Student> bestStudentAsyncJob = bestListAsyncJob.map(new Func<List<Student>, Student>() {
            @Override
            public Student call(List<Student> students) {
                return findBest(students);
            }
        });

        AsyncJob<String> storeNameAsyncJob = bestStudentAsyncJob.flatMap(new Func<Student, AsyncJob<String>>() {
            @Override
            public AsyncJob<String> call(Student students) {
                return stuApiWrapper.store(students);
            }
        });
        return storeNameAsyncJob;
    }

    private Student findBest(List<Student> students) {
        return Collections.max(students);
    }
}

lambda实现

  其实代码已经很简单了,接着我们通过lambda方式实现如下:

package com.jared.emrxandroidstudy;

import java.util.Collections;
import java.util.List;

/**
 * Created by jared on 16/3/13.
 */
public class StudentHelper {

    StuApiWrapper stuApiWrapper;

    public AsyncJob<String> saveTheBestStudent(String query) {
        AsyncJob<List<Student>> bestListAsyncJob = stuApiWrapper.queryStudents(query);
        AsyncJob<Student> bestStudentAsyncJob = bestListAsyncJob.map(students -> findBest(students));
        AsyncJob<String> storeNameAsyncJob = bestStudentAsyncJob.flatMap(student -> stuApiWrapper.store(student));
        return storeNameAsyncJob;
    }

    private Student findBest(List<Student> students) {
        return Collections.max(students);
    }
}

RxJava实现

  其实这些都可以使用RxJava来实现,修改StuApiWrapper:

package com.jared.emrxandroidstudy;

import java.util.List;

import rx.Observable;
import rx.Subscriber;

/**
 * Created by jared on 16/3/13.
 */
public class StuApiWrapper {
    StuApi stuApi;

    public Observable<List<Student>> queryStudents(String query) {
        return Observable.create(new Observable.OnSubscribe<List<Student>>() {
            @Override
            public void call(Subscriber<? super List<Student>> subscriber) {
                stuApi.queryStudents(query, new StuApi.StudentsCallback() {
                    @Override
                    public void onStudentsListGetOk(List<Student> students) {
                        subscriber.onNext(students);
                    }

                    @Override
                    public void onQueryFailed(Exception e) {
                        subscriber.onError(e);
                    }
                });
            }
        });
    }

    public Observable<String> store(final Student student) {
        return Observable.create(new Observable.OnSubscribe<String>() {
            @Override
            public void call(Subscriber<? super String> subscriber) {
                stuApi.store(student, new StuApi.StoreCallback() {
                    @Override
                    public void onStudentStored(String name) {
                        subscriber.onNext(name);
                    }

                    @Override
                    public void onStoreFailed(Exception e) {
                        subscriber.onError(e);
                    }
                });
            }
        });
    }
}

  修改StudentHelper:

package com.jared.emrxandroidstudy;

import java.util.Collections;
import java.util.List;

import rx.Observable;

/**
 * Created by jared on 16/3/13.
 */
public class StudentHelper {

    StuApiWrapper stuApiWrapper;

    public Observable<String> saveTheBestStudent(String query) {
        Observable<List<Student>> bestListObservable = stuApiWrapper.queryStudents(query);
        Observable<Student> bestStudentObservable = bestListObservable.map(students -> findBest(students));
        Observable<String> storeNameObservable = bestStudentObservable.flatMap(student -> stuApiWrapper.store(student));
        return storeNameObservable;
    }

    private Student findBest(List<Student> students) {
        return Collections.max(students);
    }
}

  我们看到,通过简单的转化我们可以把异步操作给抽象出来。这个抽象出来的东西可以被用来操作和组合异步操作就像简单的方法那样。通过这种方法我们可以摆脱嵌套的回调,在处理异步结果时也能手动处理错误的传递。