MVP模式分析

Google Sample MVP

Android架構(gòu)藍圖

目前Android主流的開發(fā)架構(gòu):原生開發(fā)(MVC),MVP,MVVM等
今天簡單的說一下我對于Android架構(gòu)的了解和對Google MVP的認識分析知残。
說Android就不得不提到Java,Android的應(yīng)用層和Java有著不解之緣练湿,Android應(yīng)用層參考Java的實現(xiàn)并且進行了很多的優(yōu)化番枚,比如大家都熟悉的JVM與Android虛擬機。其實Android開發(fā)也是對于Java GUI圖形界面開發(fā)的優(yōu)化村砂,完成了MVC的分層烂斋,用布局文件(xml文件)完成了對于視圖布局的抽象和優(yōu)化。
以下是自己結(jié)合自己實際開發(fā)中的經(jīng)驗對MVP的一些感悟础废。
先看一下對于MVP架構(gòu)的目錄結(jié)構(gòu)圖

mvp.png

Note: in a MVP context, the term "view" is overloaded:

· The class android.view.View will be referred to as "Android View"
· The view that receives commands from a presenter in MVP, will be simply called "view".

注意:在MVP的上下文里汛骂,“view”一詞有多重含義:
· android.view.View被稱為“Android View”
· 在MVP中,從presenter接收命令的view將被簡單地稱為“view”评腺。

It uses fragments for two reasons:

· The separation between Activity and Fragment fits nicely with this implementation of MVP: the Activity is the overall controller that creates and connects views and presenters.
· Tablet layout or screens with multiple views take advantage of the Fragments framework.

(MVP中的View實現(xiàn))使用Fragment有兩個原因:
· Activity與Fragment之間的分離很好的符合了MVP的實現(xiàn):Activity作為整體控制器來創(chuàng)建和連接views與presenters帘瞭。
· 平板布局或者屏幕上有多個views的布局可以很好的利用Fragments框架。

/**
 * This specifies the contract between the view and the presenter.
 */
public interface AddEditTaskContract {

    interface View extends BaseView<Presenter> {

        void showEmptyTaskError();

        void showTasksList();

        void setTitle(String title);

        void setDescription(String description);

        boolean isActive();
    }

    interface Presenter extends BasePresenter {

        void saveTask(String title, String description);

        void populateTask();
    }
}

**
 * Listens to user actions from the UI ({@link AddEditTaskFragment}), retrieves the data and updates
 * the UI as required.
 */
public class AddEditTaskPresenter implements AddEditTaskContract.Presenter,
        TasksDataSource.GetTaskCallback {

    ……
    /**
     * 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
     */
    public AddEditTaskPresenter(@Nullable String taskId, @NonNull TasksDataSource tasksRepository,
            @NonNull AddEditTaskContract.View addTaskView) {
        mTaskId = taskId;
        mTasksRepository = checkNotNull(tasksRepository);
        mAddTaskView = checkNotNull(addTaskView);

        mAddTaskView.setPresenter(this);
    }

    @Override
    public void start() {
        if (!isNewTask()) {
            populateTask();
        }
    }

    @Override
    public void saveTask(String title, String description) {
        if (isNewTask()) {
            createTask(title, description);
        } else {
            updateTask(title, description);
        }
    }

    ……
}
public class AddEditTaskFragment extends Fragment implements AddEditTaskContract.View {

    public static final String ARGUMENT_EDIT_TASK_ID = "EDIT_TASK_ID";

    private AddEditTaskContract.Presenter mPresenter;

    private TextView mTitle;

    private TextView mDescription;

    public static AddEditTaskFragment newInstance() {
        return new AddEditTaskFragment();
    }

    public AddEditTaskFragment() {
        // Required empty public constructor
    }

    @Override
    public void onResume() {
        super.onResume();
        //BasePresenter接口的方法蒿讥,主要完成數(shù)據(jù)的初始化
        mPresenter.start();
    }

//在構(gòu)造函數(shù)中調(diào)用完成presenter和View的關(guān)聯(lián)
    @Override
    public void setPresenter(@NonNull AddEditTaskContract.Presenter presenter) {
        mPresenter = checkNotNull(presenter);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        FloatingActionButton fab =
                (FloatingActionButton) getActivity().findViewById(R.id.fab_edit_task_done);
        fab.setImageResource(R.drawable.ic_done);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPresenter.saveTask(mTitle.getText().toString(), mDescription.getText().toString());
            }
        });
    }
    ……
}
public class AddEditTaskActivity extends AppCompatActivity {

        ……
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.addtask_act);

      ……
        // Create the presenter
        new AddEditTaskPresenter(
                taskId,
                Injection.provideTasksRepository(getApplicationContext()),
                addEditTaskFragment);
    }

        ……
}

在構(gòu)造方法中調(diào)用View的setPresenter方法與View建立聯(lián)系

Activity 在項目中是一個全局控制著蝶念,負責(zé)創(chuàng)建View以及Presenter,并將兩者聯(lián)系起來

Contract這個類是首次出現(xiàn)于google的mvp示例中芋绸,以前的MVP模式并未見到媒殉,這個類定義了View接口和Presenter接口為對方的實例提供的方法。容易的可以看出View和Presenter之間的操作摔敛。

這個契約類的好處是方便接口統(tǒng)一管理廷蓉、修改,同時马昙,內(nèi)容清晰桃犬,一目了然刹悴,維護起來也方便。
先來看看官方的代碼目錄(這里只是功能模塊的目錄攒暇,不包括測試模塊土匀,畢竟這里分析的是官方的實現(xiàn)代碼)

屏幕快照 2016-10-25 下午2.39.35.png

tasks 包可以顯示任務(wù)列表
taskdetail包顯示任務(wù)詳情
addedittask包添加和編輯任務(wù)
statistics包用來顯示任務(wù)的完成情況
data包數(shù)據(jù)模塊對應(yīng)mvp的M
Util包就是通用的方法。

我們的項目中也運用到了mvp的思想扯饶,其實mvp并沒有固定的寫法恒削,正確的去理解架構(gòu)的思想,都可以有自己獨特的mvp寫法尾序。

簡單說一下我們項目中MVP架構(gòu)的實現(xiàn)
我們項目中的實現(xiàn)與Google實現(xiàn)不同只有Presenter接口和接口的實現(xiàn)類钓丰,Presenter與View直接的調(diào)用關(guān)聯(lián)關(guān)系同過泛型參數(shù)實現(xiàn),這使得我們的具有類文件更少的優(yōu)勢每币,同時不可避免帶來了相互調(diào)用比較模糊携丁;(接口相互之間持有引用)
在我們的實現(xiàn)之中,F(xiàn)ragment或者Activity相當于作為了默認的View層(接口以及實現(xiàn)類)
我們的項目實現(xiàn)里面沒有明顯的Repository層兰怠,上層(activity/fragment/presenter)不需要知道數(shù)據(jù)的細節(jié)(或者說 - 數(shù)據(jù)源)梦鉴,來自于網(wǎng)絡(luò)、數(shù)據(jù)庫揭保,亦或是內(nèi)存等等肥橙。如此,一來上層可以不用關(guān)心細節(jié)秸侣,二來底層可以根據(jù)需求修改存筏,不會影響上層,兩者的分離用可以幫助協(xié)同開發(fā)
還有一個不同的是我們的Presenter加入了生命周期味榛,我認為這一定程度上增加了Presenter層的復(fù)雜程度(以及Onclick事件的處理AZ)

有時候我們還面臨一個問題椭坚,接口數(shù)據(jù)模型不一致,View不能方便的復(fù)用
在MVVM的模式中ViewModel的存在很好的解決了這個問題搏色;
MVVM的實現(xiàn)類似于觀察者模式善茎,采用了數(shù)據(jù)模型與xml布局文件綁定的形式;
很多MVVM的框架都是實現(xiàn)了布局文件與代碼的雙向綁定频轿,xml既可以調(diào)用java的代碼垂涯,改變數(shù)據(jù)模型的同時UI也能自己改變。
Google 也給出了MVP與DataBinding
t is based on the todo-mvp sample and uses the Data Binding library to display data and bind UI elements to actions.

It doesn't follow a strict Model-View-ViewModel or a Model-View-Presenter pattern, as it uses both View Models and Presenters.

The Data Binding Library saves on boilerplate code allowing UI elements to be bound to a property in a data model.

Layout files are used to bind data to UI elements
Events are also bound with an action handler
Data can be observed and set up to be updated automatically when needed
Diagram
MVVM對比與MVP的最大優(yōu)勢就是更加解耦UI邏輯與業(yè)務(wù)邏輯. View與ViewModel的耦合, 要弱于View與Presenter的耦合. View是ViewModel的消費者, 當修改UI時, 導(dǎo)致較少地修改ViewModel. 根據(jù)業(yè)務(wù)關(guān)注點(Concern), 設(shè)置更加多的高內(nèi)聚View與ViewModel, 在多個頁面中共享與替換.

MVVM使業(yè)務(wù)邏輯更加徹底地分離, 使用DataBinding分離UI顯示與UI邏輯, 實現(xiàn)View與ViewModel的一對多, ViewModel與Model的多對多, 模塊復(fù)用更加完備, 進一步提高可測試性.

MVP 的優(yōu)缺點
任何事務(wù)都存在兩面性航邢,MVP當然也不列外集币,我們來看看MVP的優(yōu)缺點。
優(yōu)點:

  1. 模塊職責(zé)劃分明顯翠忠,層次清晰

  2. Presenter可以復(fù)用,一個Presenter可以用于多個View乞榨,而不需要更改Presenter的邏輯(當然是在View的改動不影響業(yè)務(wù)邏輯的前提下)

  3. 利于測試驅(qū)動開發(fā)秽之。以前的Android開發(fā)是難以進行單元測試的(雖然很多Android開發(fā)者都沒有寫過測試用例当娱,但是隨著項目變得越來越復(fù)雜,沒有測試是很難保證軟件質(zhì)量的考榨;而且近幾年來Android上的測試框架已經(jīng)有了長足的發(fā)展——開始寫測試用例吧)跨细,在使用MVP的項目中Presenter對View是通過接口進行,在對Presenter進行不依賴UI環(huán)境的單元測試的時候河质〖讲眩可以通過Mock一個View對象,這個對象只需要實現(xiàn)了View的接口即可掀鹅。然后依賴注入到Presenter中散休,單元測試的時候就可以完整的測試Presenter應(yīng)用邏輯的正確性。

  4. View可以進行組件化乐尊。在MVP當中戚丸,View不依賴Model。這樣就可以讓View從特定的業(yè)務(wù)場景中脫離出來扔嵌,可以說View可以做到對業(yè)務(wù)完全無知限府。它只需要提供一系列接口提供給上層操作。這樣就可以做到高度可復(fù)用的View組件痢缎。

  5. 增加了Contract接口胁勺,便于模塊功能的管理和擴展。

缺點:

  1. Presenter中除了應(yīng)用邏輯以外独旷,還有大量的View->Model署穗,Model->View的手動同步邏輯,造成Presenter比較笨重势告,維護起來會比較困難蛇捌。

  2. 由于對視圖的渲染放在了Presenter中,所以視圖和Presenter的交互會過于頻繁咱台。 UI復(fù)雜的界面Presenter過多地渲染了視圖络拌,往往會使得它與特定的視圖的聯(lián)系過于緊密。一旦視圖需要變更回溺,那么Presenter也需要變更了春贸。

  3. 額外的代碼復(fù)雜度及使用、學(xué)習(xí)成本遗遵。

MVP架構(gòu)更好地分離項目職責(zé), 解除業(yè)務(wù)邏輯與UI邏輯之間的耦合. 對于小型項目而言, 與設(shè)計模式類似, 使用接口會導(dǎo)致過度設(shè)計, 增加代碼量. 當處理復(fù)雜頁面時, Presenter層會包含大量UI邏輯與業(yè)務(wù)邏輯, 顯得非常冗余, 違反單一職責(zé)原理.

關(guān)于presenter一直持有Activity對象導(dǎo)致的內(nèi)存泄漏問題
只要用過mvp這個問題可能很多人都知道萍恕。寫mvp的時候,presenter會持有view车要,如果presenter有后臺異步的長時間的動作允粤,比如網(wǎng)絡(luò)請求,這時如果返回退出了Activity,后臺異步的動作不會立即停止类垫,這里就會有內(nèi)存泄漏的隱患司光,所以會在presenter中加入一個銷毀view的方法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末悉患,一起剝皮案震驚了整個濱河市残家,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌售躁,老刑警劉巖坞淮,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異陪捷,居然都是意外死亡回窘,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門揩局,熙熙樓的掌柜王于貴愁眉苦臉地迎上來毫玖,“玉大人,你說我怎么就攤上這事凌盯「斗悖” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵驰怎,是天一觀的道長阐滩。 經(jīng)常有香客問我,道長县忌,這世上最難降的妖魔是什么掂榔? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮症杏,結(jié)果婚禮上装获,老公的妹妹穿的比我還像新娘。我一直安慰自己厉颤,他們只是感情好穴豫,可當我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著逼友,像睡著了一般精肃。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上帜乞,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天司抱,我揣著相機與錄音,去河邊找鬼黎烈。 笑死习柠,一個胖子當著我的面吹牛匀谣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播津畸,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼振定,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了肉拓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤梳庆,失蹤者是張志新(化名)和其女友劉穎暖途,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體膏执,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡驻售,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了更米。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片欺栗。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖征峦,靈堂內(nèi)的尸體忽然破棺而出迟几,到底是詐尸還是另有隱情,我是刑警寧澤栏笆,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布类腮,位于F島的核電站,受9級特大地震影響蛉加,放射性物質(zhì)發(fā)生泄漏蚜枢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一针饥、第九天 我趴在偏房一處隱蔽的房頂上張望厂抽。 院中可真熱鬧,春花似錦丁眼、人聲如沸筷凤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嵌施。三九已至,卻和暖如春莽鸭,著一層夾襖步出監(jiān)牢的瞬間吗伤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工硫眨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留足淆,地道東北人巢块。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像巧号,于是被迫代替她去往敵國和親族奢。 傳聞我的和親對象是個殘疾皇子敦姻,可洞房花燭夜當晚...
    茶點故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,133評論 25 707
  • Android App的設(shè)計架構(gòu):MVC,MVP,MVVM與架構(gòu)經(jīng)驗談1. 架構(gòu)設(shè)計的目的1.1 通過設(shè)計使程序模...
    天空在微笑閱讀 4,147評論 1 20
  • 轉(zhuǎn)載至:http://www.reibang.com/p/9a6845b26856 “Android MVP 詳解...
    SnowDragonYY閱讀 10,322評論 5 241
  • 作者:李旺成 時間:2016年4月3日 “Android MVP 詳解(下)”已經(jīng)發(fā)布纠拔,歡迎大家提建議。 MVP ...
    diygreen閱讀 128,872評論 86 1,321
  • 一 偶然經(jīng)過蒲公英擁簇的花叢催享,心弦靈巧靠欢,雀躍廊敌。不經(jīng)意,掠入眼簾的一點血紅门怪,拮取一顆紅豆骡澈,用殷紅的情絲塵封,花季紛飛...
    杌倏閱讀 505評論 0 1