淺析MVP中model層設計【從零開始搭建android框架系列(7)】

淺析MVP中model層設計【從零開始搭建android框架系列(7)】

字數2492 閱讀6206 評論12 

更多及時技術資訊,歡迎關注我的微博 :Anthony

原文鏈接:Anthony的簡書博客
推薦鏈接:安卓架構文章合集

1 前言

在本系列文章從零開始搭建android框架系列之前我多次提到了官方mvp項目的構建。並應用到了項目MVPCommon中。但是細心的你肯定都會發現,之前的文章都在整體上對MVP 的使用進行了說明,卻對其中的model層一言帶過。包括數據也是大多采用假數據。

使用了MVP,我們肯定不會再像以前網絡訪問數據,SharedPreference保存數據,本地數據庫保存,緩存數據等的處理分散於每個activity或者fragment之間。數據的獲取、存儲、數據狀態變化都將是是Model層的任務。RxJava,Retrofit,EventBus,SqlBrite等技術都會在後續得到分析和使用。

下面的文章將會從一些優秀的模板代碼分析出發,研究MVP中model層的設計,後續的文章將會在這些基礎上繼續分析和使用。

這裏整理網絡上也有不少關於這方面優秀文章,將會在參考資料中給出。


2 google官方mvp中model層設計

之前兩篇文章分別對官方google官方架構MVP解析與實戰Google官方MVP+Dagger2架構詳解項目進行了解析,並沒有對其中的model層進行分析,這裏單獨抽取出來和大家共同學習一下。其實該項目中Model層最大的特點是被賦予了數據獲取的職責,與我們平常Model層只定義javabean,實體對象截然不同。實例中,數據的獲取、存儲、數據狀態變化都是Model層的任務,Presenter會根據需要調用該層的數據處理邏輯並在需要時將回調傳入。

我們來看TaskDetailPresenter 的 start() 方法:

 @Override
    public void start() {
        openTask();
    }

    private void openTask() {
        // 判空處理
        if (null == mTaskId || mTaskId.isEmpty()) {
            mTaskDetailView.showMissingTask();
            return;
        }
        // 更新狀態
        mTaskDetailView.setLoadingIndicator(true);
        // 獲取該條Task數據
        mTasksRepository.getTask(mTaskId, new TasksDataSource.GetTaskCallback() {
            @Override
            public void onTaskLoaded(Task task) {
                // The view may not be able to handle UI updates anymore
                // View已經被用戶回退
                if (!mTaskDetailView.isActive()) {
                    return;
                }
                // 獲取到task數據,並更新UI
                mTaskDetailView.setLoadingIndicator(false);
                if (null == task) {
                    mTaskDetailView.showMissingTask();
                } else {
                    showTask(task);
                }
            }

            @Override
            public void onDataNotAvailable() {
                // The view may not be able to handle UI updates anymore
                // 顯示數據獲取失敗時的狀態
                if (!mTaskDetailView.isActive()) {
                    return;
                }
                mTaskDetailView.showMissingTask();
            }
        });
    }

可以看到的是presenter中調用了mTaskDetailView.setLoadingIndicator(true);中更新view層的狀態之後,接着調用mTasksRepository.getTask......獲取數據,並且在數據獲取成功和失敗後,分別處理回調方法onTaskLoaded,onDataNotAvailable,並在其中更新view。將數據的操作完全交給TasksRepository處理。

我們接着看 TasksRepository 中的getTask() 方法,

@Singleton
public class TasksRepository implements TasksDataSource {
......
    private final TasksDataSource mTasksRemoteDataSource;

    private final TasksDataSource mTasksLocalDataSource;

    @Inject
    TasksRepository(@Remote TasksDataSource tasksRemoteDataSource,
            @Local TasksDataSource tasksLocalDataSource) {
        mTasksRemoteDataSource = tasksRemoteDataSource;
        mTasksLocalDataSource = tasksLocalDataSource;
    }
 ......

    /**
     * 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 void getTask(@NonNull final String taskId, @NonNull final GetTaskCallback callback) {
        checkNotNull(taskId);
        checkNotNull(callback);

        Task cachedTask = getTaskWithId(taskId);

        // 緩存獲取數據,並回調
        if (cachedTask != null) {
            callback.onTaskLoaded(cachedTask);
            return;
        }

        // 本地獲取數據
        mTasksLocalDataSource.getTask(taskId, new GetTaskCallback() {
            @Override
            public void onTaskLoaded(Task task) {
                callback.onTaskLoaded(task);
            }

            @Override
            public void onDataNotAvailable() {
            //本地獲取數據失敗,使用網絡數據
                mTasksRemoteDataSource.getTask(taskId, new GetTaskCallback() {
                    @Override
                    public void onTaskLoaded(Task task) {
                        callback.onTaskLoaded(task);
                    }

                    @Override
                    public void onDataNotAvailable() {
                        callback.onDataNotAvailable();
                    }
                });
            }
        });
    }


}

我們發現 TasksRepository 維護了兩個數據源,一個是本地mTasksLocalDataSource,一個是遠程mTasksRemoteDataSource。從getTask方法中可以看到這裏首先從緩存獲取數據,如果獲取成功則直接回調·callback.onTaskLoaded(cachedTask);`,接着從本地獲取,獲取失敗後再從網絡獲取 ,這也和處理圖片的三級緩存策略一樣。

我們發現TasksRepository類都實現了 TasksDataSource 接口:

public interface TasksDataSource {

    interface LoadTasksCallback {

        void onTasksLoaded(List<Task> tasks);

        void onDataNotAvailable();
    }

    interface GetTaskCallback {

        void onTaskLoaded(Task task);

        void onDataNotAvailable();
    }

    void getTasks(@NonNull LoadTasksCallback callback);

    void getTask(@NonNull String taskId, @NonNull GetTaskCallback callback);

    void saveTask(@NonNull Task task);

    void completeTask(@NonNull Task task);

    void completeTask(@NonNull String taskId);

    void activateTask(@NonNull Task task);

    void activateTask(@NonNull String taskId);

    void clearCompletedTasks();

    void refreshTasks();

    void deleteAllTasks();

    void deleteTask(@NonNull String taskId);
}

可以發現我們正是在這裏TasksDataSource中定義了內部接口GetTaskCallback。從而實現TasksRepository中數據獲取的時候的回調。

爲了簡化整個模板代碼的操作,這裏只在getTasks()getTask()方法中添加了回調並不是說其他地方不需要數據的回調。

還記得之前的Google官方MVP+Dagger2架構詳解文章講解的整個實例app的TasksRepositoryComponent在整個應用的Application中初始化。

@Singleton
@Component(modules = {TasksRepositoryModule.class, ApplicationModule.class})
public interface TasksRepositoryComponent {
    TasksRepository getTasksRepository();
}

我們可以看到我們將 TasksRepositoryModule放在mock下,做mock測試,看下圖:



我們也在TasksRepositoryModule中提供了數據的實例,也正是在mock中提供了假數據FakeTasksRemoteDataSource替換了TasksRemoteDataSource.

@Module
public class TasksRepositoryModule {

    @Singleton
    @Provides
    @Local
    TasksDataSource provideTasksLocalDataSource(Context context) {
        return new TasksLocalDataSource(context);
    }

    @Singleton
    @Provides
    @Remote
    TasksDataSource provideTasksRemoteDataSource() {
        return new FakeTasksRemoteDataSource();
    }
}

到這裏也就完成了整個數據model層的提供,我們也可以看到Dagger2再次顯示了它的威力。

總結:

最後,我們再來看這張圖。Fragment作爲View,View和Presenter通過Activity來進行關聯,Presenter對數據的調用是通過TasksRepository來完成的,而TasksRepository維護着它自己的數據源和實現。


到這裏你肯定明白了爲什麼我們需要Repository層了---->屏蔽底層細節。
上層(activity/fragment/presenter)不需要知道數據的細節(或者說 - 數據源),來自於網絡、數據庫,亦或是內存等等。如此,一來上層可以不用關心細節,二來底層可以根據需求修改,不會影響上層,兩者的分離用可以幫助協同開發。

android-boilerplate 中model層的設計

如果你關注安卓架構,之前肯定關注過Android Application Architecture(翻譯文章Android應用架構- 小鄧子的簡書)這篇文章。其中的示例項目android-boilerplate也是一個基於MVP架構的框架,加入EventBus(Otto),RxJava,Retrofit,SqlBrite等。其中的model層有很多值得借鑑的地方。這裏再從這整個架構圖來學習一下相應的思路。


View(視圖)層:Activities,Fragment以及ViewGroup等在這一層。處理用戶的交互和輸入事件,並且觸發Presenter中的相應操作。

Presenter層 :Presenters 訂閱(subscibe) RxJava的Observables,負責處理訂閱週期,處理由DataManager提供的數據,並調用View層中的相應方法展示數據。

Model (數據)層: 負責獲取,保存,緩存以及修改數據。負責與本地數據庫,其他數據存儲,restful APIs,以及第三方SDKs 交互。在此架構中,Model層被劃分爲兩個部分:許多helpers類和一個 DataManager.helpers類的數量在不同的工程中不盡相同,但是每個都有自己的功能。比如:通過SharedPreferences與數據進行交互的PreferHelper,通過SqlBrite提供與數據庫交互的DatabaseHelper,DataManager結合並且轉化不同的Helpers類爲Rx操作符,向Presenter層提供Observables類型的數據(provide meaningful data to the Presenter),並且同時處理數據的併發操作(group actions that will always happen together.)。這一層也包含實際的model類,用於定義當前數據架構。

因此,我們可以看到的優點是:
1 Activity和Fragment變得非常輕量。他們唯一的職責就是建立/更新UI和處理用戶事件。因此,他們變得更容易維護。

2 RxJava的Observable和操作符避免了嵌套回調的出現,同時如果數據model層出現數據錯誤,我們也會在presenter層得到處理,在presenter層調用相應的view層的方法顯示錯誤信息。

3 現在我們通過模擬View Layer可以很容易的編寫出單元測試。之前這些代碼是View Layer的一部分,所以我們很難對它進行單元測試。整個架構變得測試友好。

4 如果DataManager變得臃腫,我們可以通過轉移一些代碼到Presenter來緩解這個問題。

5 通過引入 Event Bus(事件總線,這個項目使用的是otto)。它允許我們在Data Layer中發送事件,以便View Layer中的多個組件都能夠訂閱到這些事件。比如DataManager中的退出登錄方法可以發送一個事件,訂閱這個事件的多個Activity在接收到該事件後就能夠更改它們的UI視圖,從而顯示一個登出狀態。

6 這裏的DataManager也扮演了官方示例項目中Respository的作用,屏蔽底層細節。

7 引入Dagger2 ,添加依賴注入,實現組件的重用,也就使得測試變得更加容易.

未完待續......

參考鏈接:
從零開始的Android新項目5 - Repository層(上) Retrofit、Repository組裝
從零開始的Android新項目6 - Repository層(下) Realm、緩存、異常處理
android architecture architecture guidelines
Android應用架構- 小鄧子的簡書
Android官方MVP架構項目解析
從零開始搭建android框架系列
google官方架構MVP解析與實戰
Google官方MVP+Dagger2架構詳解
Android Application Architecture
10 完美的安卓 model 層架構(上)
11 完美的安卓 model 層架構(下)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章