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

Android開發之MVP模式的學習

編輯:關於Android編程

Android開發之MVP模式的學習:提問:明白如何選擇開發框架,和為什麼要學MVP模式觀察:比較MVC模式和MVP模式,理解MVP模式的概念使用:通過一個例子,學習如何使用MVP模式總結。

提問

首先自問自答:

在Android開發中如何選擇開發框架呢?

首先要知道為什麼要選擇開發框架?目的如下:

代碼可讀維護性好方便測試 框架的核心思想:解耦 分層:縱向上的解耦模塊化:橫向上的解耦

橫向的模塊化:對大家來可能並不陌生,在一個項目建立項目文件夾的時候就會遇到這個問題,通常的做法是將相同功能的模塊放到同一個目錄下,更復雜的,可以通過插件化來實現功能的分離與加載。縱向的分層,不同的項目可能就有不同的分法,並且隨著項目的復雜度變大,層次可能越來越多

根據自己具體項目而定,如果業務簡單的項目,用MVC模式就可以了,不管是什麼開發框架,都是為了更好的提高工作效率,是手段,方法,不是目的,不要為了設計而設計,否則浪費時間,精力,得不償失。

為什麼要學習MVP模式,很重要嗎?

為什麼要思考這問題?因為時間短暫,想要做的事情很多,必須學會取捨,才能更好的做好事情。

我有個原則:

最重要的事情,只有一件,要麼不做,要麼就做得最好

如果沒有學習MVP模式,會怎麼樣?

好處:

可以做其他,自己認為更重要的事情

壞處:

思維方面:對於一些基於MVP模式優秀項目,但是看不懂啊,閱讀大神優秀思想的作品,

無法享受思維上的樂趣,怎麼能阻礙我們成為大神的前進道路呢?不可原諒,beat it !

工作方面:如果項目業務很復雜,使用MVC模式,項目開發效率,管理,代碼可讀性低,測試和維護時間長,工作效率低

所以從否定角度,證明學習MVP模式是重要,是必須的,於是花時間,專門學習,研究MVP模式,運用到工作項目中,提高工作效率

如何學習MVP模式?

1)如何認識MVP模式?(觀察現象)

回顧MVC模式

比較MVC模式和MVP模式的區別,通過比較區別,顯出MVP模式的特點,優勢

2)如何使用MVP模式?(使用方法)

通過例子分析

觀察

回顧MVC模式

View:對應於布局文件Model:業務邏輯和實體模型Controllor:對應於Activity,或Fragment 如下圖所示 MVC之間的數據是如何流動的? 首先View控件設置監聽事件,在哪裡設置呢?可以在Controller中進行設置,比如在Activity中設置按鈕的點擊事件,也可以在自定義控件中進行設置,比如在Listview中設置滑動處理事件。這些事件是什麼?可以通知Controller去操作Model,更新Model數據,也可以直接更新Model數據,也可以是Controller更新View數據 舉例:用戶對界面進行操作,比如點擊,滑動界面(事先注冊的View),監聽的View被響應,告訴Controler進行處理響應事件,比如操作Model,訪問網絡數據,更新Model對象,然後更新界面View的數據,或控件的狀態變化

MVC模式存在的問題:

數據據綁定的操作,事件處理的代碼都在Activity中,造成了Activity既像View又像Controller,導致Activity負責本來應該是View負責的工作(控件的狀態改變等等),又要負責操作Model層,導致Activity的責任太重,業務多,代碼太多,維護需要更多時間。 於是MVP模式出現了,解決MVC模式的Controller責任太重的問題

概念

當將架構改為MVP以後,Presenter的出現,將Actvity視為View層,Presenter負責完成View層與Model層的交互。現在是這樣的:

View 對應於Activity,負責顯示數據,View的繪制以及與用戶交互Model 業務邏輯和實體模型Presenter 負責完成View於Model間的交互,處理著程序各種邏輯的分發,收到View層UI上的反饋命令、定時命令、系統命令等指令後分發處理邏輯交由Model層做具體的業務操作。

如圖所示:

有圖觀察可知:MCV模式與MVP模式最明顯的區別就是,

MVC中是允許Model和View進行交互的,而MVP中很明顯,Model與View之間的交互由Presenter完成。還有一點就是Presenter與View之間的交互是通過接口的

MVP的優點

1. 降低耦合度,實現了Model和View真正的完全分離,可以修改View而不影響Modle

2. 模塊職責劃分明顯,層次清晰

3. 隱藏數據

4. Presenter可以復用,一個Presenter可以用於多個View,而不需要更改Presenter的邏輯(當然是在View的改動不影響業務邏輯的前提下)

5. 利於測試驅動開發。

6. View可以進行組件化。在MVP當中,View不依賴Model。這樣就可以讓View從特定的業務場景中脫離出來,可以說View可以做到對業務完全無知。它只需要提供一系列接口提供給上層操作。這樣就可以做到高度可復用的View組件。

7. 代碼靈活性

MVP的缺點:

1. Presenter中除了應用邏輯以外,還有大量的View->Model,Model->View的手動同步邏輯,造成Presenter比較笨重,維護起來會比較困難。

2. 由於對視圖的渲染放在了Presenter中,所以視圖和Presenter的交互會過於頻繁,view裡的行為太過復雜,應對復雜場景勢必造成代碼行為膨脹

3. 如果Presenter過多地渲染了視圖,往往會使得它與特定的視圖的聯系過於緊密。一旦視圖需要變更,那麼Presenter也需要變更了。

4. 額外的代碼復雜度及學習成本


使用

例子:實現效果:簡單登陸界面功能,輸入姓名,密碼,然後點擊登陸按鍵,提示登陸結果,點擊清除按鍵,清除輸入內容

(一)Model層

1)分析Model數據模型,實體類有什麼屬性和行為。

比如:這個例子中,登陸功能,需要一個用戶模型User

package com.example.juphome.mvpdemo.Login.bean;

/**
 * Created by Juphome on 2017/1/15.
 */
public class User {
    private String username ;
    private String password ;

    public String getUsername()
    {
        return username;
    }

    public void setUsername(String username)
    {
        this.username = username;
    }

    public String getPassword()
    {
        return password;
    }

    public void setPassword(String password)
    {
        this.password = password;
    }

}

2)定義操作具體業務類的回調接口:OnLoginListener,用於回調監聽登陸結果狀態

package com.example.juphome.mvpdemo.Login.biz;

import com.example.juphome.mvpdemo.Login.bean.User;

/**
 * Created by Juphome on 2017/1/15.
 */
public interface OnLoginListener {
    void loginSuccess(User user);

    void loginFailed();
}
定義操作業務類的抽象接口:IUserBiz
package com.example.juphome.mvpdemo.Login.biz;

import com.example.juphome.mvpdemo.Login.bean.User;

/**
 * Created by Juphome on 2017/1/15.
 */
public interface OnLoginListener {
    void loginSuccess(User user);

    void loginFailed();
}
實現操作業務類的抽象接口:UserBiz
package com.example.juphome.mvpdemo.Login.biz;

import com.example.juphome.mvpdemo.Login.bean.User;

/**
 * Created by Juphome on 2017/1/15.
 */
public class UserBiz implements IUserBiz{

    @Override
    public void login(final String username, final String password, final OnLoginListener loginListener) {
        //模擬子線程耗時操作
        new Thread()
        {
            @Override
            public void run()
            {
                try
                {
                    Thread.sleep(2000);
                } catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
                //模擬登錄成功
                if ("zhy".equals(username) && "123".equals(password))
                {
                    User user = new User();
                    user.setUsername(username);
                    user.setPassword(password);
                    loginListener.loginSuccess(user);
                } else
                {
                    loginListener.loginFailed();
                }
            }
        }.start();

    }
}

(二)View層和Presenter層的接口

定義契約類(接口)

使用契約類來統一管理view與presenter的所有的接口,這種方式使得view與presenter中有哪些功能,一目了然,維護起來也很方便。

2.1 如何定義UserBizContract接口中的View的接口呢?去觀察功能上的操作,然後考慮

該操作需要什麼?(getUserName, getPassword)該操作的結果,對應的反饋?(toMainActivity, showFailedError)該操作過程中對應的友好的交互?(showLoading, hideLoading)

2.2 如何定義UserBizContract中的presenter接口呢?

Presenter是用作Model和View之間交互的橋梁,那麼應該有什麼方法呢?

其實也是主要看該功能有什麼操作,比如本例,兩個操作:login和clear。

package com.example.juphome.mvpdemo.Login.biz;

import com.example.juphome.mvpdemo.Login.bean.User;

/**
 * Created by Juphome on 2017/1/15.
 */
public interface UserBizContract {

    interface IUserLoginView {
        String getUserName();

        String getPassword();

        void clearUserName();

        void clearPassword();

        void showLoading();

        void hideLoading();

        void toMainActivity(User user);

        void showFailedError();

    }

    interface Presenter{
        void login();
        void clear();
    }

}


(三)View層和Presenter層的接口實現

定義presenter接口的實體類

package com.example.juphome.mvpdemo.Login.presenter;

import android.os.Handler;

import com.example.juphome.mvpdemo.Login.bean.User;
import com.example.juphome.mvpdemo.Login.biz.IUserBiz;
import com.example.juphome.mvpdemo.Login.biz.OnLoginListener;
import com.example.juphome.mvpdemo.Login.biz.UserBiz;
import com.example.juphome.mvpdemo.Login.biz.UserBizContract;


/**
 * Created by Juphome on 2017/1/15.
 */
public class UserLoginPresenter implements UserBizContract.Presenter {
    private IUserBiz userBiz;
    private UserBizContract.IUserLoginView userLoginView;
    private Handler mHandler = new Handler();

    public UserLoginPresenter(UserBizContract.IUserLoginView userLoginView)
    {
        this.userLoginView = userLoginView;
        this.userBiz = new UserBiz();
    }


    @Override
    public void login() {
        userLoginView.showLoading();
        userBiz.login(userLoginView.getUserName(), userLoginView.getPassword(), new OnLoginListener()
        {
            @Override
            public void loginSuccess(final User user)
            {
                //需要在UI線程執行
                mHandler.post(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        userLoginView.toMainActivity(user);
                        userLoginView.hideLoading();
                    }
                });

            }

            @Override
            public void loginFailed()
            {
                //需要在UI線程執行
                mHandler.post(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        userLoginView.showFailedError();
                        userLoginView.hideLoading();
                    }
                });

            }
        });
    }

    @Override
    public void clear() {
        userLoginView.clearUserName();
        userLoginView.clearPassword();
    }


}


定義View的實體類

最後讓Model-View-Presenter動態聯系起來

讓Activity實現View的接口IUserLoginView,MVP中的View其實就是Activity,調用Presenter,處理業務操作,Presenter處理過程中,通知Activity界面友好提示,顯示緩沖數據圖標,Presenter處理業務操作完成後,回調通知Activity結果狀態,失敗或成功的數據

package com.example.juphome.mvpdemo.Login.view;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Toast;

import com.example.juphome.mvpdemo.Login.bean.User;
import com.example.juphome.mvpdemo.Login.biz.UserBizContract;
import com.example.juphome.mvpdemo.Login.presenter.UserLoginPresenter;
import com.example.juphome.mvpdemo.R;

/**
 * Created by Juphome on 2017/1/15.
 */
public class UserLoginActivity extends Activity implements UserBizContract.IUserLoginView {
    private EditText mEtUsername, mEtPassword;
    private Button mBtnLogin, mBtnClear;
    private ProgressBar mPbLoading;

    private UserLoginPresenter mUserLoginPresenter = new UserLoginPresenter(this);
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_user_login);

        initViews();
    }

    private void initViews()
    {
        mEtUsername = (EditText) findViewById(R.id.id_et_username);
        mEtPassword = (EditText) findViewById(R.id.id_et_password);

        mBtnClear = (Button) findViewById(R.id.id_btn_clear);
        mBtnLogin = (Button) findViewById(R.id.id_btn_login);

        mPbLoading = (ProgressBar) findViewById(R.id.id_pb_loading);

        mBtnLogin.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                mUserLoginPresenter.login();
            }
        });

        mBtnClear.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                mUserLoginPresenter.clear();
            }
        });
    }


    @Override
    public String getUserName()
    {
        return mEtUsername.getText().toString();
    }

    @Override
    public String getPassword()
    {
        return mEtPassword.getText().toString();
    }

    @Override
    public void clearUserName()
    {
        mEtUsername.setText("");
    }

    @Override
    public void clearPassword()
    {
        mEtPassword.setText("");
    }

    @Override
    public void showLoading()
    {
        mPbLoading.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideLoading()
    {
        mPbLoading.setVisibility(View.GONE);
    }

    @Override
    public void toMainActivity(User user)
    {
        Toast.makeText(this, user.getUsername() +
                " login success , to MainActivity", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void showFailedError()
    {
        Toast.makeText(this,
                "login failed", Toast.LENGTH_SHORT).show();
    }

}

使用MVP模式的步驟:

定義mode層的實體類,操作業務類的接口操作業務類完成的結果狀態監聽接口定義view的接口。定義Presenter接口實現定義好的View接口和Presenter接口讓MVP動起來

Model-View-Presenter的動態過程:

讓Acivity實現view接口,在Activity中創建Presenter引用對象,用Presenter對象,操作Model的業務邏輯處理,Model層處理完成後,通過業務監聽回調接口,告訴Presenter處理業務的結果如何,最後Persenter通過VIew接口,讓Activity進行頁面數據更新

總結

最後,我們再來看這張圖。Activity作為View,View和Presenter在Activity中來進行關聯,Presenter與Model之間雙向交互,Model與View之間沒有直接接觸,達到解耦的作用

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