人之因此能,是相信能。android
說到MVP,你們應該都不陌生了,因爲其高度解等等優勢,愈來愈多的項目使用這個設計模式。然而,優勢雖在,缺點也很多,其中一個就是類多了不少,並且V與P直接要項目通訊,那麼P就得持有V得實例,但若是活動掛掉了,若是沒有對V進行釋放,還有致使內存溢出得問題,並且,那麼多的接口函數,看獲得人眼花繚亂,也使得不少人在使用這個模式的時候望而尚步。數據庫
迴歸正題,最近在進行代碼重構,決定採用MVP模式進行開發。若是咱們不進行封裝,單純地簡單使用MVP來開發,這要就會出現如上的問題,接口和類多並且重複。和別人協同開發也存在問題。那麼對MVP模式進行封裝就顯得很重要了。固然,一千我的中有一千個哈姆雷特,這裏提供一下個人思路,供你們參考。設計模式
MVP模式至關於在MVC模式中又加了一個Presenter用於處理模型和邏輯,將View和Model徹底獨立開,在android開發中的體現就是activity僅用於顯示界面和交互,activity不參與模型結構和邏輯。bash
使用MVP模式會使得代碼多出一些接口可是使得代碼邏輯更加清晰,尤爲是在處理複雜界面和邏輯時,咱們能夠對同一個activity將每個業務都抽離成一個Presenter,這樣代碼既清晰邏輯明確又方便咱們擴展。固然若是咱們的業務邏輯自己就比較簡單的話使用MVP模式就顯得,沒那麼必要。因此咱們不須要爲了用它而用它,具體的仍是要要業務須要。微信
其實,簡而言之:view就是UI,model就是數據處理,而persenter則是他們的紐帶。網絡
MVP模式仍是存在一些不足之處的,最大的不足就是類的快速增多,但相對於MVC的臃腫、MVP的高度解耦來講,類的增多可能就灑灑水啦~~~框架
上圖介紹:ide
Contract:契約類,一個功能模塊中View接口、Model接口和請求數據回調統一在對應模塊的Contract中定義,便於管理。函數
ViewInterface: view層接口,定義了view中的UI操做工具
ModelInterface: model層接口,定義了model負責的數據操做方法,如請求接口,操做數據庫等
CallbackInterface: model層操做數據完成後的回調
BasePersenter: Persenter父類,主要是對相關view的獲取,銷燬等操做
View: view層實現類,主要就是Activity或Fragment,負責UI展現和事件響應
Model: model層實現類,就是依據業務,請求對應接口或數據庫,並將結果返給回調CallBack
Persenter: persenter層類,負責業務邏輯處理,view將響應傳給persenter,persenter負責調用model,並將結果返回給view供其展現
/**
* Description: Presenter的根父類
* Created by jia on 2016/10/27.
* 人之因此能,是相信能
*/
public abstract class BasePresenter<T> {
//View接口類型的軟引用
protected Reference<T> mViewRef;
public void attachView(T view) {
//創建關係
mViewRef = new SoftReference<T>(view);
}
protected T getView() {
return mViewRef.get();
}
public boolean isViewAttached() {
return mViewRef != null && mViewRef.get() != null;
}
public void detachView() {
if (mViewRef != null) {
mViewRef.clear();
}
}
}
複製代碼
先來觀察下這個base類:
先設置一泛型T,T爲與presenter相關的view。BasePresenter中持有一個view的軟引用。
在關聯方法中將view對象傳入,並存入軟引用中,建立獲取、取消關聯和判斷方法。
至於使用軟引用,是爲了防止所持的view都銷燬了,但presenter一直持有,致使內存泄漏。
view的封裝,主要是BaseActivity和BaseFragment的封裝。
public abstract class BaseActivity<V, T extends BasePresenter<V>> extends FragmentActivity {
public String TAG = getClass().getSimpleName() + "";
protected T mPresenter;
public Context mContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
initActivityView(savedInstanceState);
mContext = BaseActivity.this;
//建立presenter
mPresenter = createPresenter();
// presenter與view綁定
if (null != mPresenter) {
mPresenter.attachView((V) this);
}
findViewById();
getData();
}
/**
* 關於Activity的界面填充的抽象方法,須要子類必須實現
*/
protected abstract void initActivityView(Bundle savedInstanceState);
/**
* 加載頁面元素
*/
protected abstract void findViewById();
/**
* 建立Presenter 對象
*
* @return
*/
protected abstract T createPresenter();
protected abstract void getData();
@Override
protected void onDestroy() {
super.onDestroy();
...
if (null != mPresenter) {
mPresenter.detachView();
}
}
}
複製代碼
BaseActivity設置兩個泛型——V和P,明顯地,分別表明對應的View和Presenter。
其持有一個BasePresenter,在onCreated方法中,使用createPresenter方法返回對應的BasePresenter的子類,咱們就可使用了。
這裏注意一下view和presenter的處理:在onCreated中建立Presenter對象,但其內部的view軟引用仍是空;在onResume中關聯view,此時presenter已經持有view的軟引用;固然,還須要在onDestroy中取消關聯。
至於其餘的封裝就再也不介紹了,相信你們確定還有更優的封裝方法。
public abstract class BaseFragment<V, T extends BasePresenter<V>> extends Fragment {
public String TAG = getClass().getSimpleName() + "";
private static final String STATE_SAVE_IS_HIDDEN = "STATE_SAVE_IS_HIDDEN";
protected T mPresenter;
//定義一個View用來保存Fragment建立的時候使用打氣筒工具進行的佈局獲取對象的存儲
protected View view;
/**
* 當Fragment進行建立的時候執行的方法
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPresenter = createPresenter();//建立presenter
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putBoolean(STATE_SAVE_IS_HIDDEN, isHidden());
}
/**
* 這個方法是關於Fragment完成建立的過程當中,進行界面填充的方法,該方法返回的是一個view對象
* 在這個對象中封裝的就是Fragment對應的佈局
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = initFragmentView(inflater);
return view;
}
/**
* 這個方法當onCreateView方法中的view建立完成以後,執行
* 在inflate完成view的建立以後,能夠將對應view中的各個控件進行查找findViewById
*/
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
initFragmentChildView(view);
}
/**
* 這個方法是在Fragment完成建立操做以後,進行數據填充操做的時候執行的方法
*/
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
initFragmentData(savedInstanceState);
}
/**
* 完成打氣筒操做
*/
protected abstract View initFragmentView(LayoutInflater inflater);
/**
* 進行findViewById的操做
*
* @param view 打氣筒生成的View對象
*/
protected abstract void initFragmentChildView(View view);
/**
* 網絡數據填充的操做
*
* @param savedInstanceState
*/
protected abstract void initFragmentData(Bundle savedInstanceState);
/**
* 建立Presenter對象
*/
protected abstract T createPresenter();
@Override
public void onResume() {
super.onResume();
if (null != mPresenter) {
mPresenter.attachView((V) this);
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (null != mPresenter) {
mPresenter.detachView();
}
}
}
複製代碼
BaseFragment與BaseA相似就再也不累贅。
契約,顧名思義,規範定義,定義功能和模板。
在契約類中定義View的接口,Model的接口。由於Model將數據返給Presenter是使用回調方式,因此還須要再契約類中定義對應的回調。
具體看示例吧。
這裏咱們以登陸功能模塊爲例:
/**
* Description:
* Created by jia on 2017/12/20.
* 人之因此能,是相信能
*/
public class LoginContract {
public interface LoginView{
void onCheckFormatSuccess();
void onCheckFormatFail(String info);
void onLoginSuccess(Login login);
void onLoginFail(String errorInfo);
}
public interface LoginModel{
void login(String name,String password,LoginCallBack callBack);
}
public interface LoginCallBack{
void onSuccess(Login login);
void onFail(String errorInfo);
}
}
複製代碼
這裏定義了登陸頁面的view接口、model接口和對應的回調。
在view中,只定義與UI展現的相關方法,如檢查帳號密碼格式成功(失敗)、登陸成功(失敗)等。
model負責數據請求,因此在接口中只定義了登陸的方法。
回調定義了登陸成功仍是失敗的方法。
/**
* Description: 登陸 Model實現類
* Created by jia on 2017/12/20.
* 人之因此能,是相信能
*/
public class LoginModelImpl implements LoginContract.LoginModel {
/**
* 登陸方法
* @param name
* @param password
* @param callBack
*/
@Override
public void login(String name, String password, final LoginContract.LoginCallBack callBack) {
LoginNetUtils.getInstance().login(name, password, new BaseSubscriber<Login>() {
@Override
public void onSuccess(Login login) {
callBack.onSuccess(login);
}
@Override
public void onFail(String info) {
callBack.onFail(info);
}
});
}
}
複製代碼
建立Model實現類,重寫其登陸方法既可,將登陸接口交給回調。
/**
* Description: 登陸主持類
* Created by jia on 2017/12/20.
* 人之因此能,是相信能
*/
public class LoginPresenter extends BasePresenter<LoginContract.LoginView> {
private LoginModelImpl model;
public LoginPresenter() {
model = new LoginModelImpl();
}
/**
* 檢查格式
*
* @param name
* @param password
*/
public void checkFormat(String name, String password) {
if (TextUtils.isEmpty(name)) {
getView().onCheckFormatFail("請輸入用戶名");
} else if (TextUtils.isEmpty(password)) {
getView().onCheckFormatFail("請輸入密碼");
} else if (password.length() < 6 || password.length() > 18) {
getView().onCheckFormatFail("密碼格式不正確");
} else {
getView().onCheckFormatSuccess();
login(name, password);
}
}
/**
* 登陸
*
* @param name
* @param password
*/
public void login(String name, String password) {
model.login(name, password, new LoginContract.LoginCallBack() {
@Override
public void onSuccess(Login login) {
getView().onLoginSuccess(login);
}
@Override
public void onFail(String errorInfo) {
getView().onLoginFail(errorInfo);
}
});
}
}
複製代碼
LoginPresenter集成BasePresenter,傳入LoginView最爲泛型T。
內部持有Model實現類對象。
建立兩個方法,一個是檢查格式,一個是登陸。兩個方法就是業務的處理。
如登陸方法,登陸返回後,在回調中獲得數據,也能夠再進行一些邏輯判斷,將結果交給view的對應的方法。
注意這裏使用getView()方法就能夠,由於在父類中getView方法直接返回的對應的view實例。
/**
* 登陸界面
*/
public class LoginActivity extends BaseActivity<LoginContract.LoginView, LoginPresenter>
implements LoginContract.LoginView, View.OnClickListener {
...
@Override
protected void initActivityView(Bundle savedInstanceState) {
setContentView(R.layout.activity_login);
}
@Override
protected void findViewById() {
...
}
@Override
protected LoginPresenter createPresenter() {
return new LoginPresenter();
}
@Override
protected void getData() {
}
@Override
public void onCheckFormatSuccess() {
loading.show();
}
@Override
public void onCheckFormatFail(String info) {
RxToast.error(mContext, info).show();
}
@Override
public void onLoginSuccess(Login login) {
...
}
@Override
public void onLoginFail(String errorInfo) {
...
}
@Override
public void onClick(View view) {
...
}
...
}
複製代碼
這裏的LoginActivity就是登陸功能模塊的view,其集成BaseActivity,傳入view和presenter泛型。
實現LoginView接口,重寫接口定義好的UI方法。
在createPresenter方法中建立LoginPresenter對象並返回。這樣,就可使用mPresenter直接操做邏輯了。
再看下整個功能模塊的事件流和數據流
大體就是這樣了,有不足的地方你們多提意見。^_^
更多精彩內容,關注個人微信公衆號——Android機動車