尚學堂【官網】-西安Java培訓|c++培訓|Android培訓|安卓培訓|java視頻教程|軟件工程師|-西安雁塔尚學堂計算機學校
Android培訓/西安Android培訓  尚學堂首頁Java學院Android培訓/西安Android培訓

Android 架構設計——MVP架構實現深度解耦

www.zyopwk.live 發布人:java  |  來自:本站  |  發布時間:2017-09-20 13:47:07  |  點擊次數:1327

隨著 UI 創建技術的功能日益增強,UI 層也履行著越來越多的職責。為了更好地細分視圖(View)與模型(Model)的功能,讓 View 專注于處理數據的可視化以及與用戶的交互,同時讓 Model 只關系數據的處理,基于 MVC(Model View Controller) 模式的 MVP(Model-View-Presenter) 模式應運而生。

目前MVP模式在 Android 應用開發中越來越重要了,大家也都在討論 MVP 的理論,體系化的資料非常少,所以誕生出了本篇文章。MVP 模式是 MVC 模式的一個演化版本,MVP 模式能有效降低 View 的復雜度,避免業務邏輯被塞進 View 中,MVP 模式會解除 View 與 Model 的耦合,同時又帶來了良好的可擴展性、可測試性。

MVP 模式可以分離顯示層和邏輯層,它們之間通過接口進行通信,降低耦合。理想化的 MVP 模式可以實現同一份邏輯代碼搭配不同的顯示界面,因為它們之間并不依賴于具體,而是依賴于抽象,這使得 Presenter 可以運用于任何實現了 View 邏輯接口的 UI,使之具有更廣泛的適用性,保證了靈活度。

1.MVP模式的三種角色

角色說明
Model

主要做一些數據處理, 網路請求。Presenter 需要通過 Model 層存取、獲取數據,Model是封裝了

數據庫 Dao 層或者網絡獲取數據的角色,或者兩種數據獲取方式的集合。

Presenter

交互中間人,核心邏輯,處理 View 的業務邏輯,溝通 View 和 Model 的橋梁,Presenter 持有的

 View、Model 引用都是抽象,它從 Model 層檢索數據后返回給 View 層,使得 View 和 Model

沒有耦合,也將業務邏輯從 View 層抽取出來,經常會執行耗時操作。

View

用戶界面,Activity、Fragment 或者某個 View 控件,含有一個 Presenter 成員變量,通常 View

層需要實現一個邏輯接口,將 View 上的操作通過會轉交給 Presenter 進行實現,最后 Presenter

調用 View 邏輯接口將結果返回給 View 元素。

 

很多缺乏經驗的工程師很可能會將各種各樣的業務邏輯塞進某個 Activity、Fragment 或者自定義控件中,使得這些組件的單個類型臃腫不堪。MVP 模式可以讓 UI 界面和數據分離,職責單一,易于維護。MVP 模式也并不是一個標準化的模式,它有很多實現方式,我們也可以根據自己的需求和自己認為對的方式去修正 MVP 的實現方式,它可以隨著 Presenter 的復雜程度變化。只要保證我們是通過 Presenter 將 View 和 Model 解耦合、降低類型復雜度、各個模塊可以獨立測試、獨立變化,這就是正確的方向。

2.實際項目中MVP模式的實現

MVP 模式的實現我們以最簡單的用戶登錄為例進行說明。MVP 模式一個很大特點就是定義接口比較多,代碼量變大,下面來看一下實際項目中MVP模式的實現。

首先定義 MVP 基本的三個接口,基本上是固定寫法:

public interface IModel {
}
public interface IPresenter {
    /**
     * 綁定
     * @param view
     */
    void attachView(V view);
    /**
     * 防止內存的泄漏, 清除Presenter與Activity之間的綁定
     */
    void detachView();
    /**
     * @return 獲取View
     */
    V getIView();
}
public interface IView {
}

然后定義契約類,定義 Presenter、View 用到的一些接口方法:

public class LoginContract {
    public interface LoginView {
        String getUserName();
        String getPwd();
        void loginSuccess(LoginBean loginBean); // 登錄成功,展示數據
        void loginFail(String failMsg);
    }
    public interface LoginPresenter {
        void login(String name, String pwd); // 業務邏輯
    }
}

定義 LoginModel 類,主要做一些數據處理, 網路請求:

public class LoginModel extends BaseModel {
    private boolean isLogin = false;
    public boolean login(@NonNull String username, @NonNull String pwd, @NonNull final DataListener
            listener) {
        // 此處推薦使用 RxJava + Retrofit 進行網絡請求
        // 網絡請求成功 isLogin = true; listener.successInfo(articles);
        // 網絡請求失敗 isLogin = false; listener.failInfo(str);
        return isLogin;
    }
    // 通過接口產生信息回調
    public interface DataListener {
        void successInfo(T result);
        void failInfo(String result);
    }
}

LoginModel 的父類:

public class BaseModel implements IModel {
    // 做一些數據處理, 網路請求的初始化操作
}

然后定義交互中間人 LoginPresenter,處理 View 的業務邏輯,它是溝通 View 和 Model 的橋梁,Presenter 持有的 View、Model 引用都是抽象,且經常會執行耗時操作:

public class LoginPresenter extends BasePresenter implements
        LoginContract.LoginPresenter {
    @Override
    public void login(String name, String pwd) {
        if (!getIView().checkNull()) {
            ((LoginModel) getiModelMap().get("login")).login(name, pwd, new LoginModel
                    .DataListener() {
                @Override
                public void successInfo(LoginBean result) {
                    getIView().loginSuccess(result); // 成功
                }

                @Override
                public void failInfo(String result) {
                    getIView().loginFail(result); // 失敗
                }
            });
        }
    }
    @Override
    public HashMap getiModelMap() {
        return loadModelMap(new LoginModel());
    }
    @Override
    public HashMap loadModelMap(IModel... models) {
        HashMap map = new HashMap<>();
        map.put("login", models[0]);
        return map;
    }
}

Presenter 如果持有 Activity 的強引用,在請求結束之前 Activity 被銷毀了,那么由于網絡請求還沒有返回,導致 Presenter 一直持有 Activity 對象,使得 Activity 無法被回收,此時就容易發生內存泄漏,解決這個問題需要通過弱引用來解決,LoginPresenter 的父類 BasePresenter 如下:

public abstract class BasePresenter implements IPresenter {
    private WeakReference mViewRef; // View接口類型的弱引用
    /**
     * 建立關聯
     * @param iview
     */
    @Override
    public void attachView(IView iview) {
        mViewRef = new WeakReference(iview);
    }
    /**
     * 解除關聯
     */
    @Override
    public void detachView() {
        if (mViewRef != null) {
            mViewRef.clear();
            mViewRef = null;
        }
    }
    /**
     * 獲取View
     * @return
     */
    @Override
    public V getIView() {
        return mViewRef.get();
    }
    /**
     * 判斷是否與View建立了關聯
     * @return 建立則返回true
     */
    public boolean isViewAttached() {
        return mViewRef != null && mViewRef.get() != null;
    }
    public abstract HashMap getiModelMap();
    /**
     * @param models
     * @return
     * 添加多個model,如有需要
     */
    public abstract HashMap loadModelMap(IModel... models);
}

到這里,Model 和 Presenter 已經都有了,還差 View。Activity 實現需要 IView 和 LoginContract.LoginView 接口,并需要建立與 Presenter 之間的聯系,Activity 的業務邏輯都將交給 Presenter 進行處理,處理結果通過 LoginContract.LoginView 接口回調給 Activity 類:

public class LoginActivity extends AppCompatActivity implements IView, LoginContract.LoginView {
    LoginPresenter mPresenter;
    // 代碼省略
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        if (mPresenter != null) {
            mPresenter.attachView(this);
        }
        // 代碼省略
        // 請求數據,當請求成功后,調用 LoginContract.LoginView 的 loginSuccess 方法將數據傳遞給 View
        mPresenter.login(getUserName(), getPwd());
    }
    // 代碼省略
    @Override
    public void loginSuccess(LoginBean loginBean) {
        // 更新UI
    }
    @Override
    public void loginFail(String failMsg) {
    }
    /**
     * 注意MVP與Activity、Fragment生命周期的處理
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mPresenter != null) {
            mPresenter.detachView();
        }
    }
}

此時 MVP 的關系此時已經建立成功了。Activity 此時的作用只是做一些 View 的初始化工作,職責單一、功能簡單、易于維護。此外,Presenter 與 Model 也依賴于抽象而不是具體,使得 Model 的具體實現可以被輕易地替換。Presenter 與 View 的低耦合使得系統能夠應對 UI 的易變性問題,也使得系統的 View 模塊變得易于維護。最后 Activity 在實際開發中也會向上抽取出 BaseActivity,比較簡單,這里不再給出。

另外基于 MVP 架構,結合 RxJava、Retrofit、OkHttp編寫了一個開源框架,易理解,深度解耦,方便迭代。

當前文: Android 架構設計——MVP架構實現深度解耦
上一頁:學安卓要先學會JAVA嗎?
下一頁:android使用shapestroke描邊只保留底部
在線報名(*為必填項)
云南快乐十分开奖走势图