1.什麼是MVP? 簡單理解:就是通過Presenter將View和Model解耦
M —>Model 包括:與資料相關都屬於M層(例如:資料庫、檔案、網路、資料解析、資料儲存......) V —>View 包括:在MVC中View只是一個單純檢視,但是在MVP中(例如:Activity、Fragment、佈局) P —>Presenter 包括:排程,通過P層將我們的View層和Model層進行關聯轉換
2.MVP和設計模式有什麼區別? 舉例說明:北京國貿三期(整體架構) --- MVP 國貿三期中 窗戶設計、電梯設計、走廊設計 ...... 相當於設計模式 --- 針對具體的問題或者場景提出不同的解決方案。
3.MVP架構互動過程
- 簡單MVP登入案例
public interface MainView {
public void onLoginResult(String result);
}
複製程式碼
public class MainActivity extends AppCompatActivity implements MainView {
private MainPresenter mainPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.mainPresenter = new MainPresenter(this);
}
/**
* 其實在Android當中,本身就是一個非常典型的MVC架構
* 在Android MVC中
* M代表:資料
* C代表:activity或者Fragment
* V代表:檢視
*/
//MVP適合大專案
//MVP更加便於團隊開發
public void click(View v){
this.mainPresenter.login("kpioneer","123456");
}
@Override
public void onLoginResult(String result) {
Toast.makeText(this,result, Toast.LENGTH_LONG).show();
}
複製程式碼
/**
* Created by Xionghu on 2017/7/11.
* Desc: M層(資料、網路)
*/
public class MainModel {
public void login(String username, String pwd, final HttpUtils.OnHttpResultListener onHttpResultListener){
HttpTask httpTask = new HttpTask(new HttpUtils.OnHttpResultListener() {
@Override
public void onResult(String result) {
//解析資料
//更新UI
onHttpResultListener.onResult(result);
}
});
httpTask.execute(username,pwd,"http://www.baidu.com");
}
}
複製程式碼
public class MainPresenter {
private MainView mainView;
private MainModel mainModel;
public MainPresenter(MainView mainView) {
this.mainView = mainView;
this.mainModel = new MainModel();
}
public void login(String userName, String pwd){
this.mainModel.login(userName, pwd, new HttpUtils.OnHttpResultListener() {
@Override
public void onResult(String result) {
mainView.onLoginResult(result);
}
});
}
複製程式碼
5.分析簡單MVP登入案例的問題? 問題1: 假設Activty意外關閉,這個時候網路請求還在進行,當資料返回的時候,發現Activity(或者Fragment) 掛掉了將會造成記憶體洩漏。
解決辦法:MainPresenter提供一個銷燬mainView的方法
問題2:
專案開發當中Activity或者Fragment數量很龐大 Presenter相關類將會造成程式碼冗餘。
解決辦法: 單獨抽象出來(引出抽象類)
抽象類設計MVP架構
複製程式碼
抽象 AbsMvpPresenter
public abstract class AbsMvpPresenter<V extends IMvpView> {
private V view;
public V getView() {
return view;
}
/**
* 繫結
* @param view
*/
public void attachView(V view) {
this.view = view;
}
/**
* 解決繫結
*/
public void detachView(){
this.view =null;
}
}
複製程式碼
public interface IMvpView {
}
複製程式碼
public class MainPresenter extends AbsMvpPresenter<MainView> {
private MainModel mainModel;
public MainPresenter() {
this.mainModel = new MainModel();
}
public void login(String userName, String pwd){
this.mainModel.login(userName, pwd, new HttpUtils.OnHttpResultListener() {
@Override
public void onResult(String result) {
if(getView()!=null){
getView().onLoginResult(result);
}
}
});
}
複製程式碼
}
public abstract class MvpActivity<V extends IMvpView, P extends AbsMvpPresenter<V>> extends Activity {
private P presenter;
private V view;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (presenter == null) {
this.presenter = bindPresenter();
}
if (view == null) {
this.view = bindView();
this.presenter.attachView(this.view);
}
}
public P getPresenter() {
return presenter;
}
public V getView() {
return view;
}
public abstract P bindPresenter();
public abstract V bindView();
@Override
protected void onDestroy() {
super.onDestroy();
if (this.presenter != null) {
this.presenter.detachView();
}
}
}
複製程式碼
public class MainActivity extends MvpActivity<MainView,MainPresenter> implements MainView {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public MainPresenter bindPresenter() {
return new MainPresenter();
}
@Override
public MainView bindView() {
return this;
}
/**
* 其實在Android當中,本身就是一個非常典型的MVC架構
* 在Android MVC中
* M代表:資料
* C代表:activity或者Fragment
* V代表:檢視
*/
//MVP適合大專案
//MVP更加便於團隊開發
public void click(View v){
getPresenter().login("kpioneer","123456");
}
@Override
public void onLoginResult(String result) {
Toast.makeText(this,result, Toast.LENGTH_LONG).show();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
複製程式碼
問題三:
6.搭建MVP架構
public class MainPresenter extends MvpBasePresenter<MainView> {
private MainModel mainModel;
public MainPresenter(Context context) {
super(context);
this.mainModel = new MainModel();
}
public void login(String username, String pwd) {
this.mainModel.login(username, pwd, new HttpUtils.OnHttpResultListener() {
@Override
public void onResult(String result) {
if(isAttachView()){
getView().onLoginResult(result);
}
}
});
}
}
複製程式碼
問題:每一次在Presenter中呼叫方法都需要處理空(isAttachView())判斷 假設:50個Activity,20個Presenter,每一個Presenter裡面有20個方法,做判斷400次?
解決辦法 ----- 動態代理模式
每當呼叫這個方法的時候,我就要去監聽
複製程式碼
public interface MvpPresenter<V extends MvpView> {
/**
* 繫結檢視
* @param view
*/
public void attachView(V view);
public void dettachView();
}
複製程式碼
public class MvpBasePresenter<V extends MvpView> implements MvpPresenter<V> {
private WeakReference<Context> weakContext;
private WeakReference<V> weakView;
private V proxyView;
public MvpBasePresenter(Context context) {
this.weakContext = new WeakReference<Context>(context);
}
public Context getContext() {
return weakContext.get();
}
public V getView() {
return proxyView;
}
/**
* 用於檢測View是否為空物件
*
* @return
*/
public boolean isAttachView() {
if (this.weakView != null && this.weakView.get() != null) {
return true;
}
return false;
}
@Override
public void attachView(V view) {
this.weakView = new WeakReference<V>(view);
MvpViewInvocationHandler invocationHandler = new MvpViewInvocationHandler(this.weakView.get());
//在這裡採用動態代理
proxyView = (V) Proxy.newProxyInstance(view.getClass().getClassLoader(), view.getClass().getInterfaces(), invocationHandler);
}
@Override
public void dettachView() {
if (weakView.get() != null) {
this.weakView.clear();
this.weakView = null;
}
}
private class MvpViewInvocationHandler implements InvocationHandler {
private MvpView mvpView;
public MvpViewInvocationHandler(MvpView mvpView) {
this.mvpView = mvpView;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (isAttachView()) {
return method.invoke(mvpView, args);
}
return null;
}
}
}
複製程式碼
關鍵程式碼
public void attachView(V view) {
this.weakView = new WeakReference<V>(view);
MvpViewInvocationHandler invocationHandler = new MvpViewInvocationHandler(this.weakView.get());
//在這裡採用動態代理
proxyView = (V) Proxy.newProxyInstance(view.getClass().getClassLoader(), view.getClass().getInterfaces(), invocationHandler);
}
private class MvpViewInvocationHandler implements InvocationHandler {
private MvpView mvpView;
public MvpViewInvocationHandler(MvpView mvpView) {
this.mvpView = mvpView;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (isAttachView()) {
return method.invoke(mvpView, args);
}
return null;
}
}
複製程式碼