Android MVP 模式 架構 參考
Android MVP 模式 架構 參考
Model-View-Presenter(MVP)
MVP
的概念及優缺點網上一堆,如果大家不瞭解的可以去百度下,MVP
只是個思想,沒有固定的鐵則,所以不同人對於MVP
也有自己的理解,下面是本人對於MVP
的理解(偏向於Passive View
)
View
僅僅負責實現單純的、獨立的UI操作,儘量不要去維護資料(View
層指Activity
、Fragment
這類層級)Model
負責處理資料請求、業務邏輯,不涉及UI操作Presenter
是MVP
體系的控制中心,負責給View
和Model
安排工作 ,什麼時候呼叫Model
View
反應結果,都是Presenter
說了算View
與Model
均已介面的形式出現在Presenter
中,Presenter
通過呼叫View
與Model
的實現介面,來操作View
與Model
;同時Presenter
也是以藉口的形式出現在View
中,這樣Presenter
與View
就是通過介面相互依賴了Presenter
是主動方,View
是被動方,對於繫結到View
上的資料,不是View
呼叫Presenter
主動拉取資料,而是Presenter
主動將資料推給View
其呼叫順序如下:
- 使用者操作了介面
View
層響應使用者操作,然後向Presenter
Presenter
層接受了View
層的請求,呼叫Model
層處理業務邏輯,然後呼叫View
層將相應的結果反應出來
MVP的基類體系
上面說了那麼多,那麼如何在專案中構造MVP呢?不急,下面就是了~
首先我們先來構造一下MVP
的基類體系,畢竟如果每個專案都重新寫一套MVP
還是很麻煩的,構造一個MVP
的基類體系,再根據專案,基於已有的MVP
的基類體系,構造針對專案的MVP
才是王道,省時省力,同時基類體系很可以幫我們更清晰的理解MVP
View層的介面基類
這是Activity
和Fragment
需要實現的基類介面,裡面只是實現了一個獲取Activity
的方法,主要用於在Presenter
Context
物件時呼叫,不直接在Presenter
中建立Context
物件,最大程度的防止記憶體洩漏
public interface IBaseXView {
/**
* 獲取 Activity 物件
*
* @return activity
*/
<T extends Activity> T getSelfActivity();
}
Presenter層的介面基類
Presenter
需要實現的介面
public interface IBaseXPresenter {
/**
* 判斷 presenter 是否與 view 建立聯絡,防止出現記憶體洩露狀況
*
* @return {@code true}: 聯絡已建立<br>{@code false}: 聯絡已斷開
*/
boolean isViewAttach();
/**
* 斷開 presenter 與 view 直接的聯絡
*/
void detachView();
}
View層的基類實現
View
中通過IBaseXPresenter
,來實現View
對Presenter
的依賴,同時做了記憶體洩漏的預防處理。Activity
通過getPresenter()
來呼叫Presenter
。另外,對於Fragment
也可以仿照這樣寫。
public abstract class BaseXActivity<P extends IBaseXPresenter> extends Activity implements IBaseXView {
private P mPresenter;
/**
* 建立 Presenter
*
* @return
*/
public abstract P onBindPresenter();
/**
* 獲取 Presenter 物件,在需要獲取時才建立`Presenter`,起到懶載入作用
*/
public P getPresenter() {
if (mPresenter == null) {
mPresenter = onBindPresenter();
}
return mPresenter;
}
@Override
public Activity getSelfActivity() {
return this;
}
@Override
protected void onDestroy() {
super.onDestroy();
/**
* 在生命週期結束時,將 presenter 與 view 之間的聯絡斷開,防止出現記憶體洩露
*/
if (mPresenter != null) {
mPresenter.detachView();
}
}
}
Presenter中的基類實現
Presenter
中通過IBaseXView
,來實現Presenter
對View
的依賴,當每次對View
進行呼叫時,先使用isViewAttach
判斷一下,Presenter
與View
之間的聯絡是否還在,防止記憶體洩漏。Presenter
通過View
暴露的介面IBaseXView
,來控制View
public class BaseXPresenter<V extends IBaseXView> implements IBaseXPresenter {
// 防止 Activity 不走 onDestory() 方法,所以採用弱引用來防止記憶體洩漏
private WeakReference<V> mViewRef;
public BaseXPresenter(@NonNull V view) {
attachView(view);
}
private void attachView(V view) {
mViewRef = new WeakReference<V>(view);
}
public V getView() {
return mViewRef.get();
}
@Override
public boolean isViewAttach() {
return mViewRef != null && mViewRef.get() != null;
}
@Override
public void detachView() {
if (mViewRef != null) {
mViewRef.clear();
mViewRef = null;
}
}
}
以上是對View
和Presenter
做處理,可以非常明確的看出他們之間的依賴關係,而對於Model
,一般涉及到具體邏輯,資料請求在基類的初步構造中不用建立~~
具體專案中的MVP體系
上面是對廣大專案的MVP
體系進行構造,但是因為專案與專案之間的不同,每個專案都有自己獨特的需求,如果直接使用上述的MVP
體系,雖說也可以,但是卻沒有給開發者帶來最大程度的便利,僅僅是打了個大眾地基,我們還需要為我們要建造的大樓,添上大樓獨特的建造需求!
View
的介面
沒啥好說的,直接繼承,再針對實際專案補充一些會常用到的方法
public interface IBaseView extends IBaseXView {
/**
* 顯示正在載入 view
*/
void showLoading();
/**
* 關閉正在載入 view
*/
void hideLoading();
/**
* 顯示提示
* @param msg
*/
void showToast(String msg);
}
View
的實現
實現IBaseView
中方法,並繼承BaseXActivity
public abstract class BaseActivity<P extends IBasePresenter> extends BaseXActivity<P> implements IBaseView {
// 載入進度框
private ProgressDialog mProgressDialog;
@Override
public void showLoading(){
......
}
@Override
public void hideLoading(){
......
}
@Override
public void showToast(String msg){
......
}
@Override
protected void onDestroy() {
hideLoading();
super.onDestroy();
}
}
Presenter
的介面
本人在實際專案中對網路請求的取消用的也頻繁,所以寫上,你根據實際情況自己補充
public interface IBasePresenter extends IBaseXPresenter {
/**
* 取消網路請求
*
* @param tag 網路請求標記
*/
void cancel(Object tag);
/**
* 取消所有的網路請求
*/
void cancelAll();
}
Presenter
的實現
這裡不止實現了IBasePresenter
,,還實現了HttpResponseListener
,網路請求響應介面
public abstract class BasePresenter<V extends IBaseView, T> extends BaseXPresenter<V> implements IBasePresenter, HttpResponseListener<T>{
public BasePresenter(@NonNull V view) {
super(view);
}
@Override
public void cancel(@NonNull Object tag) {
......
}
@Override
public void cancelAll() {
......
}
/**
* 來自於 HttpResponseListener
*/
@Override
public void onSuccess(Object tag, T t) {
}
@Override
public void onFailure(Object tag, HttpFailure failure) {
}
}
HttpResponseListener
public interface HttpResponseListener<T> {
/**
* 網路請求成功
*
* @param tag 請求的標記
* @param t 返回的資料
*/
void onSuccess(Object tag, T t);
/**
* 網路請求失敗
*
* @param tag 請求的標記
* @param failure 請求失敗時,返回的資訊類
*/
void onFailure(Object tag, HttpFailure failure);
}
Model
的實現
在專案中實現常用的傳送網路請求的方法,本人在專案中使用的是Retrofit + RxJava
public class BaseModel {
/**
* 傳送網路請求
*
* @param observable
* @param callback
* @param <T>
*/
protected <T> void sendRequest(@NonNull Observable<T> observable, HttpResponseListener<T> callback) {
......
}
/**
* 傳送網路請求
*
* @param tag
* @param observable
* @param callback
* @param <T>
*/
private <T> void sendRequest(@NonNull Object tag, @NonNull Observable<T> observable, HttpResponseListener callback) {
......
}
/**
* 傳送網路請求
*
* @param observable 被觀察者
* @param observer 觀察者
* @param <T>
*/
protected <T> void sendRequest(@NonNull Observable<T> observable, @NonNull HttpObserver<T> observer) {
......
}
/**
* 傳送網路請求
*
* @param tag 請求標記
* @param observable 被觀察者
* @param observer 觀察者
* @param <T>
*/
protected <T> void sendRequest(@NonNull Object tag, @NonNull Observable<T> observable, @NonNull HttpObserver<T> observer) {
......
}
}
MVP在具體頁面中的實戰
上面已經建立好了我們的MVP
了,下面就是在真實頁面中使用了,是時候來一波操作了,let's go!!! 下面就以經典的登入頁面來舉例。
- 根據google的建議,在使用
MVP
時,我們可以建立一個契約類Contacts
public final class LoginContacts {
/**
* view 層介面
*/
public interface LoginUI extends IBaseView {
/**
* 登入成功
*/
void loginSuccess();
/**
* 登入失敗
*/
void loginFailure();
}
/**
* presenter 層介面
*/
public interface LoginPtr extends IBasePresenter{
void login(String username, String password);
}
/**
* model 層介面
*/
public interface LoginMdl {
void login(String username, String password, HttpResponseListener callbak);
}
}
其實,就是將View
、Presenter
、Model
中的實現介面寫在一起,看起來更加規範清晰,方便查詢
- LoginModel
public class LoginMdl extends BaseModel implements LoginContacts.LoginMdl{
/**
* 登入
*
* @param username 使用者名稱
* @param password 密碼
* @param callbak 網路請求回撥
*/
@Override
public void login(String username, String password, HttpResponseListener callbak) {
HashMap<String, String> map = new HashMap<>();
map.put("username", username);
map.put("password", Md5Util.encrypt(password));
RequestBody body = ReqBodyHelper.createJson(map);
// 傳送網路請求
sendRequest(HttpUtils.getApi().getLoginInfo(body),callbak);
}
}
- LoginPtr
public class LoginPtr extends BasePresenter<LoginContacts.LoginUI, LoginBean> implements LoginContacts.LoginPtr, HttpResponseListener<LoginBean> {
private LoginContacts.LoginMdl mLoginMdl;
public LoginPtr(@NonNull LoginContacts.LoginUI view) {
super(view);
// 例項化 Model 層
mLoginMdl=new LoginMdl();
}
@Override
public void login(String username, String password) {
//顯示進度條
showLoading();
mLoginMdl.login(username,password,this);
}
@Override
public void onSuccess(Object tag, LoginBean t) {
// 先判斷是否已經與 View 建立聯絡
if (isViewAttach()) {
// 隱藏進度條
hideLoading();
// 登入成功呼叫
getView().loginSuccess();
}
}
@Override
public void onFailure(Object tag, HttpFailure failure) {
if (isViewAttach()) {
// 隱藏進度條
hideLoading();
// 登入失敗呼叫
getView().loginFailure();
}
}
}
- LoginActivity
public class LoginActivity extends BaseActivity<LoginContacts.LoginPtr> implements LoginContacts.LoginUI {
private Button btn_login;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
btn_login=findViewById(R.id.btn_login);
btn_login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 向 Presenter 層傳送登入請求
getPresenter().login("admin","123456");
}
});
}
@Override
public LoginContacts.LoginPtr onBindPresenter() {
return new LoginPtr(this);
}
@Override
public void loginSuccess() {
Toast.makeText(this,"登入成功",Toast.LENGTH_LONG).show();
}
@Override
public void loginFailure() {
Toast.makeText(this,"登入失敗",Toast.LENGTH_LONG).show();
}
}
總結
MVP
是一種思想,並不是指某種固定框架,每個人都有自己獨特的理解。當前市場上除了MVP
,還有MVC
、MVVM
以及他們的變種模式!特別是當MVVM
的出現,倍受開發者的推崇,可能會讓人覺得MVP
已經沒落!其實不然,沒有哪種模式是沒落的,哪怕是MVC
,也有許多大公司仍然在堅持使用,而且使用的很好!需要根據實際專案需求以及 個人對這些模式的掌控力來選擇專案設計模式,不一定要用最新潮,自己用的順,用的好,那就是適合你的設計模式!
連結:https://www.jianshu.com/p/19283a3f61de