這幾天都在研究如何搭建一個實用穩固的MVP架構做爲快速開發的基底。 也糾結了好久Presenter層該如何複用,在網上查閱了不少資料以後仍然沒能找到一個適用的辦法,有的寫法單純是爲了presenter的複用而寫,卻給其餘模塊增負擔;有的實現的手法過於僵硬,不符合寫代碼的原則。 在看完各類奇奇怪怪的實現思路以後,本身心裏也有了一個實現presenter複用的一套方法,不過還不知道可不可行,到時擼完了可行再貼出來。git
走過路過點歌Start O(∩_∩)O Github項目地址github
這篇文章先擼一遍MVP的基本框架搭建,看完這篇文章你能學會:bash
順着個人思路來一遍,先構造基類: 首相是對View層的基類下手, IBaseView網絡
package com.example.administrator.mvpframedemo.base;
public interface IBaseView {
}
複製代碼
實不相瞞,這個我一個方法都沒定義。看到網上有不少人會把showToast()等這樣的方法定義在這裏喔我就不一樣意了,由於這些過重複固定的我以爲放在基類讓每一個子類去實現實在麻煩,因此我怎麼作呢?我把showToast這樣重複的實現放在BaseActivity;下面一塊兒看一下 BaseActivity架構
public abstract class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
initBeforeCreate();
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
init(savedInstanceState);
initData();
initView();
logic();
}
protected abstract void initBeforeCreate();
protected abstract int getLayoutId();
protected abstract void init(Bundle savedInstanceState);
protected abstract void initData();
protected abstract void initView();
protected abstract void logic();
protected void showToast (String toastStr) {
Toast.makeText(this, toastStr, Toast.LENGTH_SHORT).show();
};
}
複製代碼
這是在沒啥好說,這裏釋放了更BaseActivity有關的,而不是跟Mvp有關的。至於你瞭解的BaseActivity中要處理presenter綁定,解綁這樣的操做我會另外建一個BaseMvpActivity來作專門針對Mvp的處理的。保持這樣結構的整潔仍是感受神清氣爽的;固然BaseMvpActivity仍是要繼承BaseActivity的;萬一哪每天收的說這個模塊我要用MVC之類的時候也好處理一點。 接下來就是 BasePresenter框架
public class BasePresenter<V extends IBaseView> {
private WeakReference<V> mViewRef;
public V mView;
public void attachView(V view) {
mViewRef = new WeakReference<V>(view);
mView = mViewRef.get();
}
public void detachView() {
mViewRef.clear();
mView = null;
}
}
複製代碼
這裏定義了attachView()以及detachView()兩個接口;這是爲啥?還有爲何要有mViewRef? 首先說爲何要attachView()?這個其實只要你敲過一些簡單的mvp的代碼就會知道,每一次都要寫這樣的代碼:異步
MainActivity{
// 這一句代碼作了兩個事情,①View層建立本身適合的Presenter,②而後把本身傳給Presenter完成二者的綁定
Presenter presenter = new Presenter(MainActivity.this);
}
複製代碼
那麼attachView()就是完成②的事情,至於①是留給View層本身去實現的,後面會說到。 因此attachView()誕生的緣由就瞭解了,那麼detachView()設計的緣由呢? detachView()還有mViewRef的出現都是爲了解決內存泄漏而存在的。那麼是怎麼解決內存泄漏的存在呢? 當一個Activity在顯示的時候退出了,GC在感受內存緊張的時候會想把這個Activity給回收掉,可是此時presenter對象是持有Activity對象的,因此GC就沒辦法回收了,這樣就存在泄漏的隱患了。這種持有activity對象而引發內存泄漏是很是常見的緣由。 因此咱們使用deacttach()以及mViewRef(弱引用)來解除二者的綁定,讓GC爲所欲爲。那何時解綁呢?在Activity的onDestroy()生命週期的時候合適。 那麼接下來就是: BaseMvpActivityide
public abstract class BaseMvpActivity<T extends BasePresenter> extends BaseActivity implements IBaseView {
protected T mPresenter;
@Override
protected void init(Bundle savedInstanceState) {
mPresenter = bindPresenter();
mPresenter.attachView(this);
}
protected abstract T bindPresenter();
@Override
protected void onDestroy() {
super.onDestroy();
mPresenter.detachView();
}
}
複製代碼
這個BaseMvpActivity裏面作了針對Mvp的事情,包括定義bindPresenter()建立本身合適的presenter,而後執行presenter.attachView(this),將二者進行綁定;最後在onDestroy()方法中接觸二者的綁定。ui
--------------------------------------人工分割線---------------------------------------------------------------------- 到這裏,關於Mvp的基類設計好像就差很少了。 而後來模擬看看實際上要進行的業務: 登陸頁面要請求登陸 第一步:在constract(合約層,維護P層與V層的關係) 創建一個LoginConstractthis
public interface LoginContract {
abstract class LoginPresenter extends BasePresenter<LoginView>{
public abstract void login(String name, String password);
}
interface LoginView extends IBaseView {
void showTips(String str);
}
}
複製代碼
在合約裏面定義LoginPresenter以及LoginView的接口;實現交給其餘地方。 在這裏開發者就要明白登陸的邏輯,例如:首先View利用presetner發起登陸(login)請求,請求完成以後View要給用戶顯示結果(showTips);因此在上面定義的接口也是這麼來的。 實現: LoginPresenter:
public class LoginPresenter extends LoginContract.LoginPresenter {
ILoginModel loginModel;
@Override
public void login(String name, String password) {
loginModel = new LoginModel();
loginModel.login(name, password, new LoginCallBack());
}
private class LoginCallBack implements ICallBack<LoginDomain, Exception> {
@Override
public void onSuccess(LoginDomain result) {
mView.showTips("登陸成功");
}
@Override
public void onFail(Exception error) {
mView.showTips("登陸失敗");
}
}
}
複製代碼
簡簡單單,login()方法須要用到model層去幫忙獲取數據。因此實例化合適的model,而後調用它取數據的接口。 我通常會把model層分爲interface以及impl兩層,一個定義接口,一個實現。 不過關於Presenter與Model層的交互問題須要說明一下:由於Model層取數據大多未異步操做,因此一般使用接口回調的方式。因此在調用Model的login()取數據的時候傳遞一個回調對象來實現異步。 LoginModel:
public class LoginModel implements ILoginModel {
@Override
public void login(String username, String password, ICallBack<LoginDomain, Exception> callBack) {
// 模擬一部網絡獲取
try {
Thread.sleep(2000);
callBack.onSuccess(new LoginDomain("周潤發","123"));
} catch (InterruptedException e) {
e.printStackTrace();
callBack.onFail(new Exception());
}
}
}
複製代碼
到了這裏其實只有一些細節的問題了,關於presenter與model層的ICallBack回調如何統一規範等等都是小事。 最重要的是presenter如何複用?敬請期待我以後的博文。
最後是個人項目的目錄結構: