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);
}
}
我們看到,通過簡單的轉化我們可以把非同步操作給抽象出來。這個抽象出來的東西可以被用來操作和組合非同步操作就像簡單的方法那樣。通過這種方法我們可以擺脫巢狀的回撥,在處理非同步結果時也能手動處理錯誤的傳遞。