前言
這篇文章是android-architecture源碼分析系列文章的第四篇,我們將對todo-mvp-rxjava項目Demo進行源碼分析。todo-mvp-rxjava項目是在todo?mvp項目的基礎(chǔ)上添加了RxJava響應(yīng)式編程,希望讀者有一定的Rxjava的基礎(chǔ)。
引入RxJava和Lambda
RxJava
RxJava是近來最火的一個響應(yīng)式編程的庫,在項目中引入RxJava的根本目的在于:使用RxJava可以在程序邏輯復(fù)雜的情況下,在不斷做線程調(diào)度的同時依然保持代碼簡潔。
RxJava:https://github.com/ReactiveX/RxJava
RxAndroid:https://github.com/ReactiveX/RxAndroid
在項目中引入RxJava:
compile "io.reactivex:rxjava:$rootProject.rxjavaVersion"
compile "io.reactivex:rxandroid:$rootProject.rxandroidVersion"
在寫本文時RxJava早已迭代出2.0版本,但由于todo-mvp-rxjava的Demo使用的還是1.0版本的API,這里并沒有做出修改,還是使用1.0版本。
Lambda
Java8開始引入了Lambda表達式,它讓代碼變得更加簡潔,但是也降低了代碼的可讀性。
在Android項目中引入Lambda時,需要在App下的 build.gradle 文件中輸入以下內(nèi)容:
android {
...
defaultConfig {
jackOptions {
enabled true
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
...
源碼分析
我們開始分析todo-mvp-rxjava的代碼,項目結(jié)構(gòu)其實與todo-mvp差別不大,重點就是分析RxJava在項目中的使用。
從上圖可以看到,util包里面添加了對RxJava的線程調(diào)度的封裝,我們進一步分析具體實現(xiàn)類SchedulerProvider的源碼。
/**
* Provides different types of schedulers.
*/
public class SchedulerProvider implements BaseSchedulerProvider {
@Nullable
private static SchedulerProvider INSTANCE;
// Prevent direct instantiation.
private SchedulerProvider() {
}
public static synchronized SchedulerProvider getInstance() {
if (INSTANCE == null) {
INSTANCE = new SchedulerProvider();
}
return INSTANCE;
}
@Override
@NonNull
public Scheduler computation() {
return Schedulers.computation();
}
@Override
@NonNull
public Scheduler io() {
return Schedulers.io();
}
@Override
@NonNull
public Scheduler ui() {
return AndroidSchedulers.mainThread();
}
}
SchedulerProvider類對外提供了三種不同的可調(diào)度的線程類型。
- Schedulers.computation():CPU密集型計算所使用的Scheduler。
- Schedulers.io():I/O操作(讀寫文件、讀寫數(shù)據(jù)庫、網(wǎng)絡(luò)信息交互等)所使用的 Scheduler。
- AndroidSchedulers.mainThread():運行在Android UI線程上。
BasePresenter類也基于RxJava做了更改:
public interface BasePresenter {
void subscribe();
void unsubscribe();
}
Presenter需要根據(jù)Activity或者View的生命周期來調(diào)用subscribe()和unsubscribe();
我們還是以AddEditTask模塊為例,接著分析AddEditTaskPresenter
/**
* Listens to user actions from the UI ({@link AddEditTaskFragment}), retrieves the data and updates
* the UI as required.
*/
public class AddEditTaskPresenter implements AddEditTaskContract.Presenter {
@NonNull
private final TasksDataSource mTasksRepository;
@NonNull
private final AddEditTaskContract.View mAddTaskView;
@NonNull
private final BaseSchedulerProvider mSchedulerProvider;
@Nullable
private String mTaskId;
private boolean mIsDataMissing;
@NonNull
private CompositeSubscription mSubscriptions;
/**
* Creates a presenter for the add/edit view.
*
* @param taskId ID of the task to edit or null for a new task
* @param tasksRepository a repository of data for tasks
* @param addTaskView the add/edit view
* @param shouldLoadDataFromRepo whether data needs to be loaded or not (for config changes)
*/
public AddEditTaskPresenter(@Nullable String taskId, @NonNull TasksDataSource tasksRepository,
@NonNull AddEditTaskContract.View addTaskView, boolean shouldLoadDataFromRepo,
@NonNull BaseSchedulerProvider schedulerProvider) {
mTaskId = taskId;
mTasksRepository = checkNotNull(tasksRepository);
mAddTaskView = checkNotNull(addTaskView);
mIsDataMissing = shouldLoadDataFromRepo;
mSchedulerProvider = checkNotNull(schedulerProvider, "schedulerProvider cannot be null!");
mSubscriptions = new CompositeSubscription();
mAddTaskView.setPresenter(this);
}
@Override
public void subscribe() {
if (!isNewTask() && mIsDataMissing) {
populateTask();
}
}
@Override
public void unsubscribe() {
mSubscriptions.clear();
}
...
@Override
public void populateTask() {
if (isNewTask()) {
throw new RuntimeException("populateTask() was called but task is new.");
}
mSubscriptions.add(mTasksRepository
.getTask(mTaskId)
.subscribeOn(mSchedulerProvider.computation()) //執(zhí)行在computation線程中
.observeOn(mSchedulerProvider.ui()) //回調(diào)方法執(zhí)行在主線程中
.subscribe(
// onNext
task -> {
if (mAddTaskView.isActive()) {
mAddTaskView.setTitle(task.getTitle());
mAddTaskView.setDescription(task.getDescription());
mIsDataMissing = false;
}
}, // onError
__ -> {
if (mAddTaskView.isActive()) {
mAddTaskView.showEmptyTaskError();
}
}));
}
...
}
CompositeSubscription就是用來持有所有的Subscriptions,然后在生命周期結(jié)束(unsubscribe方法)時取消所有的訂閱。
mAddTaskView.setPresenter(this);是將Presenter賦值到View中,由View的生命周期管理Presenter的狀態(tài):
- 在View的onResume()方法中調(diào)用Presenter的subscribe();
- 在View的onPause()方法中調(diào)用Presenter的unsubscribe();
Observable.subscribe綁定的時候會返回一個Subscription的對象,add到mSubscriptions里面統(tǒng)一管理。
mTasksRepository.getTask(mTaskId)返回的是Observable<Task>。Observable 即被觀察者,它決定什么時候觸發(fā)事件以及觸發(fā)怎樣的事件。
TasksRepository.getTask(taskId)方法:
/**
* Gets tasks from local data source (sqlite) unless the table is new or empty. In that case it
* uses the network data source. This is done to simplify the sample.
*/
@Override
public Observable<Task> getTask(@NonNull final String taskId) {
checkNotNull(taskId);
final Task cachedTask = getTaskWithId(taskId);
// Respond immediately with cache if available
if (cachedTask != null) {
return Observable.just(cachedTask);
}
// Load from server/persisted if needed.
// Do in memory cache update to keep the app UI up to date
if (mCachedTasks == null) {
mCachedTasks = new LinkedHashMap<>();
}
// Is the task in the local data source? If not, query the network.
Observable<Task> localTask = getTaskWithIdFromLocalRepository(taskId);
Observable<Task> remoteTask = mTasksRemoteDataSource
.getTask(taskId)
.doOnNext(task -> {
mTasksLocalDataSource.saveTask(task);
mCachedTasks.put(task.getId(), task);
});
return Observable.concat(localTask, remoteTask).first()
.map(task -> {
if (task == null) {
throw new NoSuchElementException("No task found with taskId " + taskId);
}
return task;
});
}
簡單描述getTask(taskId)方法的流程:
- 在本地數(shù)據(jù)中查詢對應(yīng)taskId的數(shù)據(jù)。
- 從遠程數(shù)據(jù)中查詢對應(yīng)taskId的數(shù)據(jù)。
- 將獲得的本地數(shù)據(jù)對象和遠程數(shù)據(jù)對象依次發(fā)送出來。
- 取得滿足指定條件的第一個數(shù)據(jù)。
- 將這條數(shù)據(jù)轉(zhuǎn)換成Task對象。
- 返回這個Task對象。
concat:接收若干個Observables,發(fā)射數(shù)據(jù)是有序的,不會交叉。
first:獲取源Observable產(chǎn)生的第一個數(shù)據(jù)或者滿足指定條件的第一個數(shù)據(jù)。
map:事件對象的直接變換。
總結(jié)
todo-mvp-rxjava是在todo-mvp的基礎(chǔ)上集成了RxJava庫,相比之下,從程序架構(gòu)看來并沒有太多的不一樣,主要區(qū)別還是在Model層對數(shù)據(jù)操作過程中進行了多次線程調(diào)度,使用到了RxJava的響應(yīng)式編程簡化了代碼流程。而本文并沒有對RxJava做過多的介紹,這也不是本文的初衷。
從RxJava的火爆程度來看,在Android項目架構(gòu)中引入RxJava,以后或許會成為常態(tài)。