此项目源码参考:
设计模式讲解参考:
MVC、MVP、MVVM三种区别_Tiger的专栏-CSDN博客
MVP是Google开源的一个设计模式,主要是为了细分视图(View)与模型(Model)的功能,让View只做两件事:
- 完成用户的交互;
- 显示界面布局,同时让Model做数据的处理,业务逻辑放到另外的一个类(Presenter)中。
下面做具体分析:
- M:M层,在项目中负责数据的处理,包括本地数据库查询,网络数据获取都在这一层中完成;
- View:V层,在项目中是UI模块,也就是各种activity/fragment,负责绘制UI元素、与用户进行交互。
- P:P层,在项目做为View与Model的桥梁,M跟V层不直接交互,M层在获取到数据之后,传递到P,P层再通过接口回调到View层,同样,View层的点击等事件,通过P层去通知M层去处理。
如下图所示:
1.MVP设计模式实践
1.1MVP基类定义
MVP设计模式下P将M和V完全隔离开,P和V是双向通信,P和M之间也是双向通信,采用面向接口编程,Fragment/Activity属于V层,Fragment/Activity会实现V接口,P通过持有的V实现通知Fragment/Activity需要进行UI更新,P间接间接持有Fragment/Activity,可能会造成内存泄露,那么如何解决呢,接下来讲解一下项目中的实践?
定义Presenter接口
我们在定义Presenter时会定义attachView()和detachView方法,然后在Activity/Fragment下onCreate()方法调用Presenter.attachView()关联持有的V(Activity/Fragment),在onDestroy()方法下调用Presenter.detachView()取消关联持有的V(Activity/Fragment),从而解决内存泄漏问题;
public interface MvpPresenter<V extends MvpView> {
@UiThread
void attachView(V view);
@UiThread
void detachView(boolean retainInstance);
}Presenter接口实现
Presenter持有V的弱引用
public class MvpBasePresenter<V extends MvpView> implements MvpPresenter<V>{
private WeakReference<V> viewRef;
public void MvpBasePresenter(){
}
@UiThread
public void attachView(V view) {
this.viewRef = new WeakReference(view);
}
@UiThread
public V getView(){
return this.viewRef == null ? null : (V)this.viewRef.get();
}
@UiThread
public void detachView(boolean retainInstance) {
if(this.viewRef.get() != null){
this.viewRef.clear();
this.viewRef = null;
}
}
}定义Activity基类
添加和移除Presenter对V的引用
public abstract class BaseMvpActivity<V extends MvpView, P extends MvpPresenter<V>>
extends AppCompatActivity implements MvpView, MvpDelegateCallback<V, P> {
protected P presenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
presenter = createPresenter();
if(presenter != null){
//Presenter关联V
presenter.attachView((V) this);
}
}
@NonNull
@Override
public abstract P createPresenter();
@Override
public P getPresenter() {
return presenter;
}
@Override
public void setPresenter(P presenter) {
this.presenter = presenter;
}
@NonNull
public V getMvpView() {
return (V) this;
}
@Override
protected void onDestroy() {
super.onDestroy();
if(presenter != null){
//Presenter取消关联V
presenter.detachView(false);
}
}
}定义MvpView通信P和V之间通信接口基类
public interface MvpView {
}定义MVP下P和V的代理类
public interface MvpDelegateCallback<V extends MvpView, P extends MvpPresenter<V>> {
@NonNull
P createPresenter();
P getPresenter();
void setPresenter(P presenter);
V getMvpView();
}1.2继承MVP基类完成业务实现
定义P和V相关的通信接口
public class MainContract {
public interface MainPresenter extends MvpPresenter<MainView> {
void requestData();
}
public interface MainView extends MvpView{
void orderSuccess();
void orderFail();
}
}Presenter实现
public class MainHomePresenter extends MvpBasePresenter<MainContract.MainView> implements MainContract.MainPresenter {
private OrderModel orderModel;
public MainHomePresenter(){
orderModel = new OrderModel();
}
@Override
public void requestData() {
orderModel.order(new OrderModel.OrderStatusListener() {
@Override
public void orderSuccess() {
//请求接口
//更新UI
getView().orderSuccess();
}
@Override
public void orderFail() {
getView().orderFail();
}
});
}
}Activity/Fragment实现View接口
public class MainActivity extends BaseMvpActivity<MainContract.MainView,
MainContract.MainPresenter> implements MainContract.MainView {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
presenter.requestData();
}
@NonNull
@Override
public MainContract.MainPresenter createPresenter() {
return new MainHomePresenter();
}
@Override
public void orderSuccess() {
//更新UI
Toast.makeText(this,"加载数据成功", Toast.LENGTH_LONG).show();
}
@Override
public void orderFail() {
//显示加载失败
}
}2.通过手动添加和移除对View的引用MVP设计总结
优点
1)解决了View的内存泄露问题,不产生直接强引用就能相互调用,所以不影响内存回收;
2)只需要关注业务的开发,针对接口去设计Model;
public interface OrderModel{
@POST("order")
OrderBean order(@Query("orderNo") String orderNo);
}3)只需要在presenter里面处理业务流程,actvity处理业务的发起和结果接受;model按照接口文档傻瓜式写上就可以了,也不需要加各种try catch;底层会帮业务处理好,直接回调给当前的View;
缺点
这样设计无法实现实现View对应多个Presenter的情况,那么接下来讲解基于反射的方式实现MVP;
3.基于反射的方式实现MVP
3.1MVP相关基类定义
创建Evm中间类,让present和View、model中间,不产生强引用关系,所以也就不会产生内存泄露的问题;再就是present和modle都可以自由定义,想定义几个就定义几个,都可以通过EVM中间类进行关联,而且采用的是反射的方法,只获取接口,所以对性能无影响,不像EventBus和ButterKnife那样要遍历所有的方法或者变量,而且如果View已经回收,则会生成一个临时该接口类型的对象,不需要是否为空判断;

EVM负责管理所有的Presenter和View
public class EVM {
private EVM(){
}
private Map<String, EasyView> views = new HashMap();
private Map<String, EasyPresenter> presenters = new HashMap();
private <T extends EasyView> T getView(Class<T> clazz){
EasyView easyView = views.get(clazz.getSimpleName());
if(easyView == null){
try {
return clazz.newInstance();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
return (T)easyView;
}
private <T extends EasyPresenter> T getPresenter(Class<T> clazz){
EasyPresenter presenter = presenters.get(clazz);
if(presenter == null){
try {
return clazz.newInstance();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
return (T)presenter;
}
private void managerView(EasyView easyView, boolean registerOrNot){
//getInterfaces()方法和Java的反射机制有关,它能够获得这个对象所实现的所有接口
Class[] classes = easyView.getClass().getInterfaces();
for(Class clazz: classes){
/**
* 如果是A.isAssignableFrom(B)
* 确定一个类(B)是不是继承来自于另一个父类(A),一个接口(A)是不是
* 实现了另外一个接口(B),或者两个类相同。主要,这里比较的维度不是实例对象,
* 而是类本身,因为这个方法本身就是Class类的方法,判断的肯定是和类信息相关的。
*
* 也就是判断当前的Class对象所表示的类,是不是参数中传递的Class对象所表示的
* 类的父类,超接口,或者是相同的类型。是则返回true,否则返回false。
*/
if(EasyView.class.isAssignableFrom(clazz)){
if(registerOrNot){
views.put(clazz.getSimpleName(), easyView);
}else{
views.remove(clazz.getSimpleName());
}
}
}
}
public static void register(EasyView easyView){
EVM.ins().managerView(easyView, true);
}
public static void unregister(EasyView easyView){
EVM.ins().managerView(easyView, false);
}
public static <T extends EasyView> T getV(Class<T> clazz){
return EVM.ins().getView(clazz);
}
public static <T extends EasyPresenter> T getP(Class<T> clazz){
return EVM.ins().getPresenter(clazz);
}
private static class InnerClass{
private static EVM evm = new EVM();
}
private static EVM ins(){
return InnerClass.evm;
}
}在BaseActivity使用
public class EasyActivity extends AppCompatActivity implements EasyView{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EVM.register(this);
}
@Override
public void success() {
}
@Override
public void fail() {
}
@Override
protected void onDestroy() {
super.onDestroy();
EVM.unregister(this);
}
}Presenter和View基类接口定义
public interface EasyPresenter {
}
public interface EasyView {
public void success();
public void fail();
}
3.2MVP基于基类实现
写一个LoginContract,把和登录有关的View和present都定义在里边
public interface LoginContract {
interface Presenter extends EasyPresenter{
void doLogin(String name, String password);
void register(String phone, String password);
void verificationCode(String phone);
}
interface LoginView extends EasyView{
void loginSuccess(User user);
void loginFail(int status, String message);
}
interface RegisterView extends EasyView{
void registerSuccess(String name, String password);
}
interface GetVerificationCodeView extends EasyView{
void getVerificationCode(String code);
}
}View实现
public class EasyLoginActivity extends EasyActivity implements LoginContract.LoginView {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_easy_login);
doLogin();
}
private void doLogin() {
EVM.getP(LoginPresenter.class).doLogin("15888888888","888888");
}
@Override
public void loginSuccess(User user) {
//登录成功
}
@Override
public void loginFail(int status, String message) {
}
}Presenter实现
public class LoginPresenter implements LoginContract.Presenter {
private LoginModel loginModel;
public LoginPresenter(){
loginModel = new LoginModel();
}
@Override
public void doLogin(String name, String password) {
loginModel.login(name, password, new LoginModel.LoginStatusListener() {
@Override
public void loginSuccess(User user) {
//登录成功
EVM.getV(LoginContract.LoginView.class).loginSuccess(user);
}
@Override
public void loginFail(int status, String message) {
//登录失败
EVM.getV(LoginContract.LoginView.class).loginFail(status,message);
}
});
}
@Override
public void register(String phone, String password) {
}
@Override
public void verificationCode(String phone) {
}
}3.3此种MVP设计方式的总结
彻底解除View、Present、model之间的关联,其中model、View、present各自想写几个就写几个,通过evm中间类关联,都可以互相调用;