Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開發之MVP模式

Android開發之MVP模式

編輯:關於Android編程

MVP模式是MVC模式在Android上的一種變體,要介紹MVP就得先介紹MVC。在MVC模式中,Activity應該屬於view這一層,而在實際開發中,它既承擔了view層,又包含了一些controller的東西。這對於開發與維護來說,都是不太友好的,耦合度太高。現在,把Activity中的View和Controller抽離出來就變成了View和Presenter,這就是MVP模式。

MVC模式

MVC模式的結構分為三部分:實體層的Mode,視圖層的View,控制層的Controller。
這裡寫圖片描述

View層就是程序的UI界面,用於向用戶展示數據以及接受用戶的輸入 Model層就是JavaBean實體類,用戶保存用戶實例數據 Controller控制器用於更新UI界面和數據實例

例如,View層接受用戶的輸入,然後通過Controller修改對應的model實例,同時,當Model實例的數據發生變化的時候,需要修改對應的UI界面,可以通過Controller達到更新界面的目的。當前,View層也可以直接更新Model數據,而不用通過Controller,這樣對於一些簡單的數據更新 小項目而言,會方便許多。

舉個簡單的例子,現在要實現一個飄雪的動態壁紙,可以給雪花定義一個實體類Snow,裡面存在XY軸的坐標,View層就是SurfaceView或其它師徒,為了實現雪花飄的效果,可以啟動一個後台線程,在線程裡不同更新snow實例裡的坐標值,這部分就是controller的工作了,controller裡還要定時更新SurfaceView上面的雪花。更進一步的話,可以在SurfaceView上監聽用戶的點擊,如果用戶點擊,只通過Controller對觸摸點周圍的Snow的坐標值進行調整,從而實現雪花在用戶點擊後彈開等效果。

MVP模式

在Android項目中,activity和fragment占據了大部分的開發工作,mvp模式就是專門為優化activity和fragment的代碼而產生的。

按照MVC的分層,activity和fragment(後面只說activity)應該屬於View層,用戶展示UI界面,接收用戶的輸入以及生命周期相關的工作。以登錄功能為例,如果把輸入 輸出以及具體實現登錄操作的代碼,都寫在一個Activity中的話,那這個Activity代碼將會顯得非常臃腫,稍微復雜一些的頁面,代碼量分分鐘就達到了上千行,並且這些功能不利於擴展復用,閱讀和維護起來也是相當頭疼的一件事。再讓我們看一下MVP模式的結構圖:
這裡寫圖片描述

MVP模式的核心思想就是把Activity中的UI邏輯抽象成view接口,把Controller相關的業務邏輯抽象成presenter接口,Model還是原來的Model。

MVP模式的作用

分離了視圖邏輯和業務邏輯,降低耦合度 Activity只處理生命周期相關的東西,代碼更加簡潔 視圖邏輯和業務邏輯分別抽象到了View和Presenter的接口中,提高了代碼的可閱讀性 Presenter被抽象成接口,可以有多種具體的實現,方便進行單元測試 把業務邏輯抽象到Presenter中,避免後台線程引用Activity導致Activity的資源無法被系統回收從而引起內存洩漏和OOM

Activity代碼變的更加簡潔

相信很多人閱讀代碼的時候,都是從Activity開始的,對著一個1000+行代碼的Activity,看了都覺得難受。

使用MVP之後,Activity就能瘦身許多了,基本上只有FindView、SetListener以及Init的代碼。其他的就是對Presenter的調用,還有對View接口的實現。這種情形下閱讀代碼就容易多了,而且你只要看Presenter的接口,就能明白這個模塊都有哪些業務,很快就能定位到具體代碼。Activity變得容易看懂,容易維護,以後要調整業務、刪減功能也就變得簡單許多。

方便進行單元測試

一般單元測試都是用來測試某些新加的業務邏輯有沒有問題,如果采用傳統的代碼風格(習慣性上叫做MV模式,少了P),我們可能要先在Activity裡寫一段測試代碼,測試完了再把測試代碼刪掉換成正式代碼,這時如果發現業務有問題又得換回測試代碼,咦,測試代碼已經刪掉了!好吧重新寫吧……

MVP中,由於業務邏輯都在Presenter裡,我們完全可以寫一個PresenterTest的實現類繼承Presenter的接口,現在只要在Activity裡把Presenter的創建換成PresenterTest,就能進行單元測試了,測試完再換回來即可。萬一發現還得進行測試,那就再換成PresenterTest吧。

避免 Activity 的內存洩露

Android APP 發生OOM的最大原因就是出現內存洩露造成APP的內存不夠用,而造成內存洩露的兩大原因之一就是Activity洩露(Activity Leak)(另一個原因是Bitmap洩露(Bitmap Leak))。

Java一個強大的功能就是其虛擬機的內存回收機制,這個功能使得Java用戶在設計代碼的時候,不用像C++用戶那樣考慮對象的回收問題。然而,Java用戶總是喜歡隨便寫一大堆對象,然後幻想著虛擬機能幫他們處理好內存的回收工作。可是虛擬機在回收內存的時候,只會回收那些沒有被引用的對象,被引用著的對象因為還可能會被調用,所以不能回收。

Activity是有生命周期的,用戶隨時可能切換Activity,當APP的內存不夠用的時候,系統會回收處於後台的Activity的資源以避免OOM。

采用傳統的MV模式,一大堆異步任務和對UI的操作都放在Activity裡面,比如你可能從網絡下載一張圖片,在下載成功的回調裡把圖片加載到 Activity 的 ImageView 裡面,所以異步任務保留著對Activity的引用。這樣一來,即使Activity已經被切換到後台(onDestroy已經執行),這些異步任務仍然保留著對Activity實例的引用,所以系統就無法回收這個Activity實例了,結果就是Activity Leak。Android的組件中,Activity對象往往是在堆(Java Heap)裡占最多內存的,所以系統會優先回收Activity對象,如果有Activity Leak,APP很容易因為內存不夠而OOM。

采用MVP模式,只要在當前的Activity的onDestroy裡,分離異步任務對Activity的引用,就能避免 Activity Leak。

MVP模式的使用

這裡寫圖片描述

上面一張簡單的MVP模式的UML圖,從圖中可以看出,使用MVP,至少需要一下幾步:
1. 創建IPresenter接口,把所有業務邏輯相關的接口都放在這裡,並創建它的實現PresenterImpl
2. 創建IView接口,把所有視圖邏輯的接口都放在這裡,其實現類是當前Activity或Fragment
3. Activity裡面包含一個IPresenter,而PresenterImpl裡包含一個IView的引用,並包含Model。Activity只保留IPresenter的引用,其它工作都放在PresenterImpl中實現
4. Model不是必須的,依照實際情況,但一定會有View和Presenter的。

通過上面的介紹,MVP的主要特點就是把Activity裡的許多邏輯都抽離到View和Presenter接口中去,並由具體的實現類來完成。這種寫法多了許多IView和IPresenter的接口,在某種程度上加大了開發的工作量,剛開始使用MVP的小伙伴可能會覺得這種寫法比較別扭,而且難以記住。其實一開始想太多也沒有什麼卵用,只要在具體項目中多寫幾次,就能熟悉MVP模式的寫法,理解TA的意圖,以及享受其帶來的好處。

MVP模式簡單示例

簡單做幾個計算器加的運算:

UI如下:
這裡寫圖片描述
項目代碼結構圖如下:
這裡寫圖片描述
包的命名和其它一樣,可以按照模塊進行命名,也可以直接按照頂層package進行明明。

首先,看一下MainActivity的代碼:

public class MainActivity extends AppCompatActivity implements ICalcView, View.OnClickListener {

    private EditText mNumber01;
    private EditText mNumber02;
    private TextView mResult;

    private ICalcPrestenter mCalcPrestener;

    private Button mBtnCalc;
    private Button mBtnClear;

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

        mNumber01 = (EditText) findViewById(R.id.et_number01);
        mNumber02 = (EditText) findViewById(R.id.et_number02);
        mResult = (TextView) findViewById(R.id.tv_result);

        mBtnCalc = (Button) findViewById(R.id.btn_calc);
        mBtnClear = (Button) findViewById(R.id.btn_clear);

        mCalcPrestener = new CalcPresenter(this);

        mBtnClear.setOnClickListener(this);
        mBtnCalc.setOnClickListener(this);

    }

    @Override
    public void clearText() {
        mNumber01.setText("");
        mNumber02.setText("");
        mResult.setText("");
    }

    @Override
    public void showResult(int result) {
        mResult.setText(String.valueOf(result));
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_calc:
                String number01 = mNumber01.getText().toString().trim();
                if (TextUtils.isEmpty(number01)) {
                    showToast("第一個數字不能為空");
                    return;
                }
                String number02 = mNumber02.getText().toString().trim();
                if (TextUtils.isEmpty(number02)) {
                    showToast("第二個數字不能為空");
                    return;
                }
                mCalcPrestener.sum(Integer.parseInt(number01), Integer.parseInt(number02));
                break;
            case R.id.btn_clear:
                mCalcPrestener.clear();
                break;
        }
    }

    private void showToast(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }
}

如上,activity只持有一個presenter的引用,實現了IView接口,具體邏輯相關的操作交由presenter進行處理。

ICalcPrestenter代碼如下:

public interface ICalcPrestenter {

    void sum(int a,int b);

    void clear();

}

其實現類CalcPrestenterImpl代碼:

public class CalcPresenter implements ICalcPrestenter {

    private ICalcView mCalcView;

    public CalcPresenter(ICalcView calcView) {
        mCalcView = calcView;
    }

    @Override
    public void sum(int a, int b) {
        mCalcView.showResult(a + b);
    }

    @Override
    public void clear() {
        mCalcView.clearText();
    }
}

presenter實現類中,持有對IView(Activity實現了IView)的引用,邏輯處理完成後,通過調用IView的視圖邏輯進行UI的更新。如果有其它Activity也需要用到相同的邏輯,可以直接進行復用,並且一個Activity中可以持有多個Presenter的引用,達到方便 耦合性低 閱讀性和維護性高的目標,這才是MVP的核心思想。

好了,簡單介紹這麼多,本文內容文字參考了網絡上部分文章,示例是根據自己的理解,做了一個超級簡單的demo,如有錯誤之處,歡迎指出~

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved