MVP模式
簡稱:MVP 全稱:Model-View-Presenter ;MVP 是從經典的模式MVC演變而來,它們的基本思想有相通的地方:Controller/Presenter負責邏輯的處理,Model提供數據,View負責顯示。
這張圖可以很清晰的看出MVP各層的職責,簡單來說
M層,即Model數據模型層,主要用來提供數據
V層,即VIew視圖層,用來展示視圖-------由Activity充當
P層,即preserter邏輯層,他是VIew層和Model層的橋樑,也是MVP模式的特點
MVP模式有哪些優點呢
MVP模式是由MVC模式演化而來,在MVC模式中,Activity即充當了View又充當了Controller,這樣就導致Activity中的代碼可能會非常的臃腫,MVP模式就很好的解決了這個問題。
具體的優點:
1、模型與視圖完全分離,我們可以修改視圖而不影響模型
2、可以更高效地使用模型,因爲所有的交互都發生在一個地方--Presenter內部
3、我們可以將一個Presenter用於多個視圖,而不需要改變Presenter的邏輯。這個特性非常的有用,因爲視圖的變化總是比模型的變化頻繁。
4、如果我們把邏輯放在Presenter中,那麼我們就可以脫離用戶接口來測試這些邏輯(單元測試)
如何使用MVP
既然MVP模式這麼好用,那我們應該如何使用呢,接下來通過一個用戶登錄的demo來解析MVP模式
項目分包:
這裏主要分成了di和ui兩個包,di處理數據邏輯即M層和P層,ui處理頁面即V層。
創建契約類
我個人認爲創建契約類的方法非常好,幫助我們管理MVP中的接口,一目瞭然。
契約類的代碼如下
public interface IContract {
public interface IView{
void ShowData(String msg);
}
public interface IPresenter<IView>{
void resqusetMesg(String name,String pwd);
void attachView(IView iView);
void deattachView(IView iView);
}
public interface IModel{
public interface CallBack{
void responseData(String msg);
}
void requestData(String name,String pwd,CallBack callBack);
}
}
非常容易可以看出,這一個接口託管了MVP三層的接口
Activity中
剛纔已經介紹過了,在MVP中Acticity主要負責的是view的操作
所以我們的Acticity需要實現View的接口,重寫View中的方法
到這裏Activity如下
public class MainActivity extends AppCompatActivity implements IContract.IView {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void ShowData(String msg) {
}
之後我們的view層應該與p層進行關聯,也就是我們的Activity中需要持有p層的引用
private IContract.IPresenter presenter;
剛剛說過,p層主要是邏輯層,處理View層的數據
所以當我們點擊按鈕之後,應該把輸入框中的內容傳到P層中進行非空判斷或者其他的邏輯判斷
而且P層要和View進行關聯,所以在持有P層的引用之後,應該調用P層的方法與View進行關聯
//綁定當前的視圖
presenter.attachView(this);
所以onCreate中如下
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
presenter = new PresenterImpl();
//綁定當前的視圖
presenter.attachView(this);
name = findViewById(R.id.text_name);
pwd = findViewById(R.id.text_pwd);
button = findViewById(R.id.btn_login);
//點擊按鈕獲取文本內容
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String name = MainActivity.this.name.getText().toString();
String pwd = MainActivity.this.pwd.getText().toString();
//傳到P層
presenter.resqusetMesg(name,pwd);
}
});
}
之後我們再看重寫的方法
Activity是負責View的,也就是UI,重寫的方法也就是更新UI的方法
所以我們重寫的方法中代碼如下
@Override
public void ShowData(final String msg) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,msg,Toast.LENGTH_SHORT).show();
}
});
}
之後我們重寫onDestroy方法,銷燬視圖
@Override
protected void onDestroy() {
presenter.deattachView(this);
super.onDestroy();
}
到這裏Activity中的所有代碼就完成了
Activity中的所有代碼如下:
public class MainActivity extends AppCompatActivity implements IContract.IView {
private IContract.IPresenter presenter;
private EditText name;
private EditText pwd;
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
presenter = new PresenterImpl();
presenter.attachView(this);
name = findViewById(R.id.text_name);
pwd = findViewById(R.id.text_pwd);
button = findViewById(R.id.btn_login);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String name = MainActivity.this.name.getText().toString();
String pwd = MainActivity.this.pwd.getText().toString();
presenter.resqusetMesg(name,pwd);
}
});
}
@Override
public void ShowData(final String msg) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,msg,Toast.LENGTH_SHORT).show();
}
});
}
@Override
protected void onDestroy() {
presenter.deattachView(this);
super.onDestroy();
}
}
P層的實現類
首先先實現P層的接口,重寫裏面的方法
public class PresenterImpl implements IContract.IPresenter<IContract.IView> {
@Override
public void resqusetMesg(String name, String pwd) {
}
@Override
public void attachView(IContract.IView iView) {
}
@Override
public void deattachView(IContract.IView iView) {
}
}
P層就是view層和model層的一個橋樑
所以在P層中需要持有VIew層和Model層的引用,並且定義一個弱引用防止內存泄漏
private IContract.IView iView;
private IContract.IModel iModel;
private WeakReference<IContract.IView> iViewWeakReference;
private WeakReference<IContract.IModel> iModelWeakReference;
接下來,看一下重寫的方法的作用
首先 resqusetMesg 方法,用來接收View傳過來的數據,並進行邏輯操作
這裏主要思路是先進行非空判斷,如果不爲空的話到Model層中去請求數據,然後通過接口回調把結果返回到View層
所以這個方法中的代碼如下
@Override
public void resqusetMesg(String name, String pwd) {
if (name !=null){
iModel.requestData(name, pwd, new IContract.IModel.CallBack() {
@Override
public void responseData(String msg) {
iView.ShowData(msg);
}
});
}
}
接下來 attachView 方法
這個方法的主要作用就是讓P層和VIew層建立聯繫
代碼如下:
@Override
public void attachView(IContract.IView iView) {
this.iView = iView;
iModel = new ModelImpl();
iModelWeakReference = new WeakReference<>(iModel);
iViewWeakReference = new WeakReference<>(iView);
}
最後 deattachView 方法
顯而易見也就是銷燬View的方法
@Override
public void deattachView(IContract.IView iView) {
iViewWeakReference.clear();
iModelWeakReference.clear();
}
至此P層就全部寫完了
P層的全部代碼如下 :
public class PresenterImpl implements IContract.IPresenter<IContract.IView> {
private IContract.IView iView;
private IContract.IModel iModel;
private WeakReference<IContract.IView> iViewWeakReference;
private WeakReference<IContract.IModel> iModelWeakReference;
@Override
public void resqusetMesg(String name, String pwd) {
if (name !=null){
iModel.requestData(name, pwd, new IContract.IModel.CallBack() {
@Override
public void responseData(String msg) {
iView.ShowData(msg);
}
});
}
}
@Override
public void attachView(IContract.IView iView) {
this.iView = iView;
iModel = new ModelImpl();
iModelWeakReference = new WeakReference<>(iModel);
iViewWeakReference = new WeakReference<>(iView);
}
@Override
public void deattachView(IContract.IView iView) {
iViewWeakReference.clear();
iModelWeakReference.clear();
}
}
M層的實現類
Model層是數據模型層,也就是主要負責請求數據的
首先先實現Model層的接口,重寫裏面的方法
public class ModelImpl implements IContract.IModel {
@Override
public void requestData(String name, String pwd, CallBack callBack) {
}
}
之後我們通過OKHttp訪問網絡數據,也可以根據需求選擇其他的網絡請求框架
通過post請求,將我們拿到的用戶名和密碼到網上進行查詢,並將查詢到的結果返回
Model層的所有代碼如下 :
public class ModelImpl implements IContract.IModel {
@Override
public void requestData(String name, String pwd, final CallBack callBack) {
String url = "https://www.zhaoapi.cn/user/login";
OkHttpClient okHttpClient = new OkHttpClient
.Builder()
.writeTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.build();
FormBody build = new FormBody
.Builder()
.add("mobile", name)
.add("password", pwd)
.build();
Request request = new Request.Builder().url(url).post(build).build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String msg = response.body().string();
callBack.responseData(msg);
}
});
}
}
思路整理
1.首先先寫一個契約類,管理MVP的全部接口
2.讓MainActivity繼承View的接口,重寫方法
3.MainActivity持有P層的引用,點擊按鈕把獲取到的內容傳到P層進行邏輯操作
4.寫P層的實現類,判斷內容,如果沒有問題把數據傳到M層
5.寫M層的實現類,主要進行請求數據的操作
6.請求成功,把結果返回View層展示
注意:內存泄漏的問題
希望這篇文章對你的MVP學習有所幫助