Android官方架構(gòu)分析(四)——todo?mvp?rxjava

前言

這篇文章是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在項目中的使用。

todo-mvp-rxjava的包結(jié)構(gòu)

從上圖可以看到,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)方法的流程:

  1. 在本地數(shù)據(jù)中查詢對應(yīng)taskId的數(shù)據(jù)。
  2. 從遠程數(shù)據(jù)中查詢對應(yīng)taskId的數(shù)據(jù)。
  3. 將獲得的本地數(shù)據(jù)對象和遠程數(shù)據(jù)對象依次發(fā)送出來。
  4. 取得滿足指定條件的第一個數(shù)據(jù)。
  5. 將這條數(shù)據(jù)轉(zhuǎn)換成Task對象。
  6. 返回這個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)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,928評論 25 709
  • 前言我從去年開始使用 RxJava ,到現(xiàn)在一年多了。今年加入了 Flipboard 后,看到 Flipboard...
    占導(dǎo)zqq閱讀 9,304評論 6 151
  • 在做播放器類似的軟件時,通常會有一個計時的功能,用來表示當(dāng)前歌曲還剩多久,這里的通常我們肯定是用UILabel來顯...
    CoderOne閱讀 745評論 0 0
  • 假設(shè)有兩個表,訂單表和產(chǎn)品表,訂單跟產(chǎn)品的關(guān)系是一對多的關(guān)系,那么在JPA中怎樣表示一對多的關(guān)系呢?實體關(guān)系一對多...
    姜小碼閱讀 17,316評論 0 3
  • 想要獲得滿意的收入得增加自己的實力和利用正確的方法賺取應(yīng)得的收入進一步實現(xiàn)財務(wù)自由。在這之前千萬不要忘了自己...
    杏兒姐閱讀 424評論 0 1

友情鏈接更多精彩內(nèi)容