Android架構設計---MVP模式第(一)篇之基本認實

版權聲明:本文為LooperJing原創文章,轉載請注明出處!

MVP 這種模式出現已經很久了,在網上有些關于 MVP 開源代碼2014年就有了,近期有關注項目架構方面的內容,于是乎,作為一個還不懂什么是 MVP 的人,那么就一定要了解一下的。網上關于 MVP 的資料其實也不少,通常都要把 MVP 和 MVC 做一下比較,我喜歡直接了當,相信有耐心看MVP的人是一定懂 MVC 的,MVC 的略過。本文的項目地址是:https://github.com/herojing/JokeMVP,下面結合項目談談MVP是個什么東西,以下就當作自己的學習總結筆記吧。

一、什么是MVP?

MVP 是 Model、Presenter、View 的縮寫,三個部分的關系如下圖所示。


效果圖

在 Android 項目中,負責界面展示的模塊(所有的 Activitiy 、Fragment以及 View 的子類)都可以劃分到 View 這個層次,所有的業務邏輯處理(請求網絡數據、數據庫讀取等)可以劃分到 Model 這個層次,為了使得 View 和 Model 之間松耦合,用 Presenter 幫助解耦。所以可以猜測,在具體實現中 Presenter 類肯定要持有 View 和 Model 的引用。現在來說一下,上圖中三個箭頭的意思。流程是這樣子的,從左到右看,比如我們剛進入一個 Activity,那么這個 Activity 做為 View 層,肯定需要通知 Presenter 加載數據,而Presenter會繼續調用Model層加載數據,等Model加載完畢后,回調給 Presenter,Presenter 持有View引用,再通知View更新界面。

二、MVP的效果

采用MVP的目的就是使得層次更加清晰,業務邏輯與 UI 分離,那么采用 MVP 以后的效果如何呢?DEMO 實現的是一個列表,效果如圖下圖所示,列表的內容是一些笑話信息。



如果上面的頁面采用 MVP 的模式進行設計的話,那么Activity中的代碼將非常清潔!請看下面。

public class MainActivity extends BaseActivity implements JokeView {

    // 不做分頁加載的操作,所以這兩個參數寫死
    public static final String  PAGE_NUM           = "1";

    public static final String  PAGE_SIZE          = "20";

    private ListView            mListView;

    private JokePresenter       mJokePresenter     = null;

    private ArrayList<JokeInfo> mJokeInfoArrayList = null;

    private JokeAdapter         mJokeAdapter;

    @Override
    public void initVariables() {
        mJokeInfoArrayList = new ArrayList<>();
        mJokePresenter = new JokePresenterImpl(this);
    }

    @Override
    public void initView() {
        setContentView(R.layout.activity_main);
        mListView = (ListView) findViewById(R.id.main_page_joke_lv);
    }

    @Override
    public void loaderData() {
        mJokeAdapter = new JokeAdapter(this, mJokeInfoArrayList);
        mListView.setAdapter(mJokeAdapter);
        //通知 Presenter 加載數據
        mJokePresenter.getJoke(PAGE_NUM, PAGE_SIZE);
    }

    @Override
    public void showLoading() {
        // TODO 顯示進度條
    }

    @Override
    public void hideLoading() {
        // TODO 隱藏進度條
    }

    @Override
    public void setJoke(Joke pJoke) {
        if (pJoke != null) {
            Joke.Result result = pJoke.getResult();
            if (result != null) {
                ArrayList<JokeInfo> jokeInfoArrayList = result.getJokeInfoArrayList();
                mJokeInfoArrayList.addAll(jokeInfoArrayList);
                mJokeAdapter.notifyDataSetChanged();
            }
        }
    }

    @Override
    public void showError() {
        TextView errorView = new TextView(this);
        errorView.setTextSize(20);
        errorView.setText("請求失敗了");
        mListView.setEmptyView(errorView);
    }
}

我重新定義了一下 Activity的“生命周期”,這個 MainActivity 繼承了 BaseActivity ,BaseActivity 的實現如下:

public abstract class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initVariables();
        initView();
        loaderData();
    }

    /**
     * 做初始化方面的工作,比如接收上一個界面的Intent
     */
    public abstract void initVariables();

    /**
     * 初始化控件
     */
    public abstract void initView();

    /**
     * 加載數據
     */
    public abstract void loaderData();

}

如果你覺的還不錯,那么可以繼續看下面了,下面將具體闡述 MVP 三個部分是如何協同操作的。

三、View層實現

在講述View層實現之前,首先看一下,項目的整體結構劃分,有個大致的感覺。如下圖所示。感覺內容還是比較多的,但是不難,一步一步的看吧!

項目結構劃分

如果要實現上面的效果,首先做一下需求分析,每一條的笑話實體類包括的屬性有笑話內容、時間;所以建立一個 Joke 實體類是很簡單的。View層承擔著界面的更新,MVP 中一般將界面更新的職責都交給一個 XXView ,我們的項目姑且叫做 JokeView 。當 Model 層請求到數據的時候,通知 Presenter 層后,Presenter 層就會調用 JokeView 進行界面的更新,所以需要一個設置笑話的方法;請求會有加載時間,所以界面要顯示 Loading ,請求結束后需要隱藏 Loading ;當斷網等異常情況發生的時候,還需要提醒用戶請求發生了錯誤,所以還需要顯示錯誤界面的方法。綜上,定義的 JokeView 接口如下;定義好 JokeView 后,就可以讓 Activity 實現 JokeView 接口,重寫里面的方法進行更新了。所以我覺得在 MVP 模式開發的過程中,最先確定的就是寫一個 XXView。

public interface JokeView {

    void showLoading();

    void hideLoading();

    void setJoke(Joke pJoke);

    void showError();
}

四、Model 層實現

在 Model 層中做的主要的工作就是請求網絡數據了。請求邏輯我用了 Volley ,具體可以看項目中是如何實現的,也是參考了網上一個開源代碼,具體的地址記不清了 。

public class JokeModelImpl implements JokeModel {


    public static final String REQUEST_SERVER_URL="http://api.jisuapi.com/xiaohua/text?";

    public static final String APPKEY="&appkey=9814b57c706d0a23";

    //http://api.jisuapi.com/xiaohua/text?pagenum=10&pagesize=3&appkey=9814b57c706d0a23
    @Override
    public void getJoke(String pNum, String pSize, final OnJokeListener pOnJokeListener) {

        VolleyRequest.newInstance().newGsonRequest(REQUEST_SERVER_URL+"pagenum="+pNum+"&"+"pagesize="+pSize+"&sort=addtime"+APPKEY,
                Joke.class, new Response.Listener<Joke>() {
                    @Override
                    public void onResponse(Joke pJoke) {
                        if (pJoke != null) {
                            pOnJokeListener.onSuccess(pJoke);
                        } else {
                            pOnJokeListener.onError();
                        }
                    }
                }, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        pOnJokeListener.onError();
                    }
                });
    }
}

其中 OnJokeListener 是 Presenter 層中定義的接口,用與通知 Presenter 層要調用 View 層更新數據。

public interface OnJokeListener {

    /**
     * 成功的時候回調
     * @param pJoke joke
     */
    void  onSuccess(Joke pJoke);

    /**
     * 失敗的時候回調
     */
    void  onError();
}

五、Presenter 層實現

在 Model 層和 View 層都定義好了之后,就可以寫 Presenter 層了,之前已經多次說過 Presenter 層作為 Model 和 View 的橋梁,需要持有 Model 和 View 的引用。Presenter 需要實現 OnJokeListener 接口,具體的實現如下:

public class JokePresenterImpl implements JokePresenter, OnJokeListener {

    // P層作為M層和V層的銜接者,需要持有JokeView和JokeModel的引用

    private JokeModel mJokeModel = new JokeModelImpl();

    private JokeView  mJokeView;

    public JokePresenterImpl(JokeView jokeView) {
        mJokeView = jokeView;
    }

    /**
     * 調用M層取數據,getJoke由所展示的界面(Activity)調用
     * 
     * @param pNum
     * @param pSize
     */
    @Override
    public void getJoke(String pNum, String pSize) {
        mJokeView.showLoading();
        mJokeModel.getJoke(pNum, pSize, this);

    }

 /**
     * 接收M層的回調,調用View 層進行界面的刷新
     *
     */
    @Override
    public void onSuccess(Joke pJoke) {
        mJokeView.setJoke(pJoke);
    }

    @Override
    public void onError() {
        mJokeView.showError();
    }
}

六、總結

最后重新梳理一下 MVP 的編寫方式。
1、 根據項目需求,寫一個 XXView 接口。然后讓對應的 Activity/Fragment 實現這個接口。View 層基本搞定!
2、編寫 Model 層,主要就是網絡數據請求了或者其他什么耗時操作,實現方式盡情發揮你的想象,但是最后一定需要用 Presenter 層定義的接口,回調給 Presenter 通知 View 層 更新數據。
3、編寫 Presenter 層,Presenter 層需要持有 View 層和 Model層的引用,并且實現 Presenter 層定義的回調接口。在回調接口中調用 View 層的代碼 進行界面更新,最重要的是,有一個調用通過Model層的方法,在此方法中,調用 Model 層請求數據。
4、回到View 層的Activity ,調用 Presenter 層獲取數據。到此完成。

備注:為了遵守面向接口編程的原則,做了一下接口的抽取。如Presenter 中 實現了 JokePresenter 接口,Model 層中實現了 JokeModel 接口。好了,如果在閱讀中,發現了有錯誤的地方,還望指正。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容