關於MVP分層架構在專案中的實際運用

wustor發表於2019-02-16

關於MVP的基本知識網上已經有很多了,但是很多都只是Demo,畢竟Demo跟實際運用之間還有無數個Debug,其實一開始我也看了很多關於MVP的介紹,其中有谷歌官方的Demo,還有很多技術部落格,那個時候就覺得自己知道什麼是MVP了,後來真正在自己專案中運用的時候,才發現並不是那麼簡單,不過經過一番折騰,逐漸對MVP有了比較深層次的認識,所以今天記錄分享一下。

MVP結構示意圖
MVP結構示意圖

我自己畫了一個關係圖,也就是最簡單的MVP結構示意圖,我在這裡並不會再解釋什麼是MVP,今天我想把所有的認知轉化為程式碼,也就是圖中的關係圖,他們之間的關係我全部都是用Callback來進行聯絡的,也就是之前說的引用。

M層

  • 這裡的M層不是傳統意義上的Model,我更傾向於認為他是一個ModelManager,就是一個資料處理中心,可以處理網路資料,資料庫,檔案以及關於Android中的四大元件的互動包含BroadcastReceiver,Service中的,都可以集中在這裡面處理,有很長一段時間對於M的認知都是覺得就是個實體類,那個時候還以為自己掌握地很透徹,最後發現自己理解的其實是不對的,MddelManager持有Presenter的引用,M處理完資料之後通過Callback回撥Presenter,Presenter由於持有View的引用,所以可以回撥View

V層

  • V就是指View(介面),往大了說是Activity或者Fragemnt,往小了說可以是一個Dialog、Toast或者Snakbar,具體就是直接跟使用者進行打交道的,View會實現Callback介面,然後傳遞給Presenter,然後Presenter會去ModelManager進行互動,然後資料處理完成之後,會通過Callback回撥回來,這樣就完成了一個閉環

P層

  • P就是Presenter,主持人其實挺形象的,就起一個客串的作用,相當於一座橋,來連線Vie跟ModelManger,很多文章是把邏輯在Presenter中進行處理的,我覺得不是很好,我認為在Modelmanager裡面處理比較好,這樣解耦的更徹底,畢竟Presenter只是箇中間的信使而已,不應該處理過多的邏輯。

關係搞清楚了,其實程式碼實現就比較簡單

M層程式碼

public class MainManager {
    private ViewCallBack mViewCallBack;

    public MainManager(ViewCallBack viewCallBack) {
        this.mViewCallBack = viewCallBack;
    }

    public void getData() {
        mViewCallBack.refreshView(1, "資料");
    }
}複製程式碼

這裡面只是寫了一個模板,可能一個介面需要多種資料處理方式,那麼就根據需求在重新定義幾個方法即可

V層程式碼

public interface ViewCallBack<V> {

    /**
     * @param code code:0:有資料,1:資料為空,2:載入失敗
     * @param data 定義好的資料型別
     */

    void refreshView(int code, V data);
}複製程式碼

這裡的V採取泛型的原因在於每個介面需要獲取的資料不一樣,所以用泛型加以區分,當一個介面需要獲取的介面很多,或者得到的資料結果型別不太一致的時候,這裡的泛型就需要用Object來指定,然後在refreshView中通過資料的型別來判斷需要重新整理的資料

P層程式碼

public class MainPresenter {
    private ViewCallBack mViewCallBack;
    private MainManager mMainManager;

    public MainPresenter(ViewCallBack viewCallBack) {
        this.mViewCallBack = viewCallBack;
        mMainManager = new MainManager(mViewCallBack);
    }

    public void getData() {
        mMainManager.getData();
    }
}複製程式碼

P層程式碼的方法跟Manager對應,然後處理M層跟V層的Callback即可,但是注意Presenter是一個物件,需要在介面銷燬的時候置空,防止記憶體洩露

我在demo中只是簡單寫了一個改變Text的值,當然在Manager裡面可以進行任何你想要的操作,因為是採用介面回撥,執行一下看看效果

MVP效果圖
MVP效果圖

持續封裝

上面的只是一個Demo,下面進行封裝抽取,方便整合到真正的專案中去

  1. M層封裝
public abstract class BaseBeanManager {

    protected ViewCallBack mViewCallBack;
    protected HashMap<String, String> paramMap;

    public BaseBeanManager(ViewCallBack modelCallBack) {
        mViewCallBack = modelCallBack;
    }
    public abstract void getData();
}複製程式碼
  1. V層封裝

 public abstract class BaseActivity<T extends BasePresenter, V> extends AppCompatActivity implements ViewCallBack<V> {
    public T presenter;//泛型定義Presenter
    @TargetApi(Build.VERSION_CODES.M)
    @Override
    protected void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView(getLayoutId());
        presenter = initPresenter();
        initViews();
        initListener();
    }

    @Override
    protected void onResume() {
        super.onResume();
        //初始化Presenter
        if (presenter == null)
            presenter = initPresenter();
            //新增Callback
        presenter.add((ViewCallBack) this);

    }
    protected abstract int getLayoutId();

    protected abstract void initViews();

    protected abstract void initListener();//初始化監聽事件

    protected abstract T initPresenter();//初始化Presenter

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //生命週期結束時銷燬callback,並置空presenter
        if (presenter != null){
             presenter.remove();
             presenter = null;

        }
    }
}複製程式碼
  1. P層封裝
public abstract class BasePresenter {
    protected BaseBeanManager mBeanManager;//定義基類manager
    protected HashMap<String, String> paramMap = new HashMap<>();
    protected ViewCallBack mViewCallBack;

    public BasePresenter(ViewCallBack viewCallBack) {
        mViewCallBack = viewCallBack;
    }


    public void add(ViewCallBack viewCallBack) {
        this.mViewCallBack = viewCallBack;
    }

    public void remove() {
        this.mViewCallBack = null;
    }
    //預設的獲取資料的方法
    protected abstract void getData();


}複製程式碼

使用

  1. M層
public class MainManager extends BaseBeanManager {

    public MainManager(ViewCallBack modelCallBack) {
        super(modelCallBack);
    }

    public void getData() {
        mViewCallBack.refreshView(1, "MVP返回的資料");
    }
}複製程式碼

2.V層

public class MainActivity extends BaseActivity<MainPresenter, String> implements ViewCallBack<String> {
    private TextView tvDemo;
    private Button btnGet;
    @Override
    protected int getLayoutId() {
        return R.layout.activity_main;
    }

    @Override
    protected void initViews() {
        tvDemo = (TextView) findViewById(R.id.tv_demo);
        btnGet = (Button) findViewById(R.id.btn_get);
    }

    public void initListener() {
        btnGet.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                presenter.getData();
            }
        });
    }

    @Override
    protected MainPresenter initPresenter() {
        return new MainPresenter(this);
    }

    @Override
    public void refreshView(int code, String data) {
        tvDemo.setText(data);
    }

}複製程式碼
  1. P層
public class MainPresenter extends BasePresenter {

    public MainPresenter(ViewCallBack viewCallBack) {
        super(viewCallBack);
    }


    public void getData() {
        mBeanManager = new MainManager(mViewCallBack);
        mBeanManager.getData();
    }
}複製程式碼

本身是不想貼這麼多程式碼的,但是隻有對比才能發現,MVP由於解耦地比較徹底,所以滿足單一職責原則,新增了很多類,封裝之後,即使新建的類變多了,每個類都需要一個相應的Presenter,但是程式碼量很少,所以看起來邏輯比較清楚

這就是我的專案中正在使用的的MVP,剛開始從MVC轉換過來的時候很不習慣,後來折騰了一陣子,發現其實挺好用的。

專案下載地址

相關文章