从入坑android至今已经有三年了,大小项目都写过,正好最近有闲,整理一下项目中的
MVP
。在项目开发中,架构就好比地基,地基打不好,建造出来的就是危楼!在小项目中这点可能还不是特别明显,毕竟就那么点代码,一个人就能从开发到维护做的很好!但是如果小项目扩展了呢?如果是中大型项目,多人合作配合呢?这时,无论开发还是维护,就得求稳,清晰明了,否则投入三个人的精力,可能还不如两个人干的好!所以,项目初期,架构设计尤为重要,今天就主要讲一下项目实战中的MVP
设计模式。
本文内容三步走
1. 构建大众化的MVP基类体系(方便以后在别的项目中的使用,不必在重写)
2. 基于MVP基类体系,根据实际项目,进行进一步封装,形成该项目型的MVP体系(主要是在基类的基础上,将项目中常用的方法等封装在基类中)
3. MVP在具体页面中的实战
Model-View-Presenter(MVP)
MVP
的概念及优缺点网上一堆,如果大家不了解的可以去百度下,MVP
只是个思想,没有固定的铁则,所以不同人对于MVP
也有自己的理解,下面是本人对于MVP
的理解(偏向于Passive View
)
-
View
仅仅负责实现单纯的、独立的UI操作,尽量不要去维护数据(View
层指Activity
、Fragment
这类层级) -
Model
负责处理数据请求、业务逻辑,不涉及UI操作 -
Presenter
是MVP
体系的控制中心,负责给View
和Model
安排工作 ,什么时候调用Model
处理逻辑,什么时候调用View
反应结果,都是Presenter
说了算 -
View
与Model
均以接口的形式出现在Presenter
中,Presenter
通过调用View
与Model
的实现接口,来操作View
与Model
;同时Presenter
也是以接口的形式出现在View
中,这样Presenter
与View
就是通过接口相互依赖了 -
Presenter
是主动方,View
是被动方,对于绑定到View
上的数据,不是View
调用Presenter
主动拉取数据,而是Presenter
主动将数据推给View
其调用顺序如下:
- 用户操作了界面
-
View
层响应用户操作,然后向Presenter
层发出请求 -
Presenter
层接受了View
层的请求,调用Model
层处理业务逻辑,然后调用View
层将相应的结果反应出来
MVP的基类体系
上面说了那么多,那么如何在项目中构造MVP呢?不急,下面就是了~
首先我们先来构造一下MVP
的基类体系,毕竟如果每个项目都重新写一套MVP
还是很麻烦的,构造一个MVP
的基类体系,再根据项目,基于已有的MVP
的基类体系,构造针对项目的MVP
才是王道,省时省力,同时基类体系很可以帮我们更清晰的理解MVP
View层的接口基类
这是Activity
和Fragment
需要实现的基类接口,里面只是实现了一个获取Activity
的方法,主要用于在Presenter
中需要使用Context
对象时调用,不直接在Presenter
中创建Context
对象,最大程度的防止内存泄漏
public interface IBaseXView {
/**
* 获取 Activity 对象
*
* @return activity
*/
<T extends Activity> T getSelfActivity();
}
Presenter层的接口基类
Presenter
需要实现的接口
public interface IBaseXPresenter {
/**
* 判断 presenter 是否与 view 建立联系,防止出现内存泄露状况
*
* @return {@code true}: 联系已建立<br>{@code false}: 联系已断开
*/
boolean isViewAttach();
/**
* 断开 presenter 与 view 直接的联系
*/
void detachView();
}
View层的基类实现
View
中通过IBaseXPresenter
,来实现View
对Presenter
的依赖,同时做了内存泄漏的预防处理。Activity
通过getPresenter()
来调用Presenter
。另外,对于Fragment
也可以仿照这样写。
public abstract class BaseXActivity<P extends IBaseXPresenter> extends Activity implements IBaseXView {
private P mPresenter;
/**
* 创建 Presenter
*
* @return
*/
public abstract P onBindPresenter();
/**
* 获取 Presenter 对象,在需要获取时才创建`Presenter`,起到懒加载作用
*/
public P getPresenter() {
if (mPresenter == null) {
mPresenter = onBindPresenter();
}
return mPresenter;
}
@Override
public Activity getSelfActivity() {
return this;
}
@Override
protected void onDestroy() {
super.onDestroy();
/**
* 在生命周期结束时,将 presenter 与 view 之间的联系断开,防止出现内存泄露
*/
if (mPresenter != null) {
mPresenter.detachView();
}
}
}
Presenter中的基类实现
Presenter
中通过IBaseXView
,来实现Presenter
对View
的依赖,当每次对View
进行调用时,先使用isViewAttach
判断一下,Presenter
与View
之间的联系是否还在,防止内存泄漏。Presenter
通过View
暴露的接口IBaseXView
,来控制View
public class BaseXPresenter<V extends IBaseXView> implements IBaseXPresenter {
// 防止 Activity 不走 onDestory() 方法,所以采用弱引用来防止内存泄漏
private WeakReference<V> mViewRef;
public BaseXPresenter(@NonNull V view) {
attachView(view);
}
private void attachView(V view) {
mViewRef = new WeakReference<V>(view);
}
public V getView() {
return mViewRef.get();
}
@Override
public boolean isViewAttach() {
return mViewRef != null && mViewRef.get() != null;
}
@Override
public void detachView() {
if (mViewRef != null) {
mViewRef.clear();
mViewRef = null;
}
}
}
以上是对View
和Presenter
做处理,可以非常明确的看出他们之间的依赖关系,而对于Model
,一般涉及到具体逻辑,数据请求在基类的初步构造中不用创建~~
具体项目中的MVP体系
上面是对广大项目的MVP
体系进行构造,但是因为项目与项目之间的不同,每个项目都有自己独特的需求,如果直接使用上述的MVP
体系,虽说也可以,但是却没有给开发者带来最大程度的便利,仅仅是打了个大众地基,我们还需要为我们要建造的大楼,添上大楼独特的建造需求!
View
的接口
没啥好说的,直接继承,再针对实际项目补充一些会常用到的方法
public interface IBaseView extends IBaseXView {
/**
* 显示正在加载 view
*/
void showLoading();
/**
* 关闭正在加载 view
*/
void hideLoading();
/**
* 显示提示
* @param msg
*/
void showToast(String msg);
}
View
的实现
实现IBaseView
中方法,并继承BaseXActivity
public abstract class BaseActivity<P extends IBasePresenter> extends BaseXActivity<P> implements IBaseView {
// 加载进度框
private ProgressDialog mProgressDialog;
@Override
public void showLoading(){
......
}
@Override
public void hideLoading(){
......
}
@Override
public void showToast(String msg){
......
}
@Override
protected void onDestroy() {
hideLoading();
super.onDestroy();
}
}
Presenter
的接口
本人在实际项目中对网络请求的取消用的也频繁,所以写上,你根据实际情况自己补充
public interface IBasePresenter extends IBaseXPresenter {
/**
* 取消网络请求
*
* @param tag 网络请求标记
*/
void cancel(Object tag);
/**
* 取消所有的网络请求
*/
void cancelAll();
}
Presenter
的实现
这里不止实现了IBasePresenter
,,还实现了HttpResponseListener
,网络请求响应接口
public abstract class BasePresenter<V extends IBaseView, T> extends BaseXPresenter<V> implements IBasePresenter, HttpResponseListener<T>{
public BasePresenter(@NonNull V view) {
super(view);
}
@Override
public void cancel(@NonNull Object tag) {
......
}
@Override
public void cancelAll() {
......
}
/**
* 来自于 HttpResponseListener
*/
@Override
public void onSuccess(Object tag, T t) {
}
@Override
public void onFailure(Object tag, HttpFailure failure) {
}
}
HttpResponseListener
public interface HttpResponseListener<T> {
/**
* 网络请求成功
*
* @param tag 请求的标记
* @param t 返回的数据
*/
void onSuccess(Object tag, T t);
/**
* 网络请求失败
*
* @param tag 请求的标记
* @param failure 请求失败时,返回的信息类
*/
void onFailure(Object tag, HttpFailure failure);
}
Model
的实现
在项目中实现常用的发送网络请求的方法,本人在项目中使用的是Retrofit + RxJava
public class BaseModel {
/**
* 发送网络请求
*
* @param observable
* @param callback
* @param <T>
*/
protected <T> void sendRequest(@NonNull Observable<T> observable, HttpResponseListener<T> callback) {
......
}
/**
* 发送网络请求
*
* @param tag
* @param observable
* @param callback
* @param <T>
*/
private <T> void sendRequest(@NonNull Object tag, @NonNull Observable<T> observable, HttpResponseListener callback) {
......
}
/**
* 发送网络请求
*
* @param observable 被观察者
* @param observer 观察者
* @param <T>
*/
protected <T> void sendRequest(@NonNull Observable<T> observable, @NonNull HttpObserver<T> observer) {
......
}
/**
* 发送网络请求
*
* @param tag 请求标记
* @param observable 被观察者
* @param observer 观察者
* @param <T>
*/
protected <T> void sendRequest(@NonNull Object tag, @NonNull Observable<T> observable, @NonNull HttpObserver<T> observer) {
......
}
}
MVP在具体页面中的实战
上面已经建立好了我们的MVP
了,下面就是在真实页面中使用了,是时候来一波操作了,let's go!!! 下面就以经典的登录页面来举例。
- 根据google的建议,在使用
MVP
时,我们可以建立一个契约类Contacts
public final class LoginContacts {
/**
* view 层接口
*/
public interface LoginUI extends IBaseView {
/**
* 登录成功
*/
void loginSuccess();
/**
* 登录失败
*/
void loginFailure();
}
/**
* presenter 层接口
*/
public interface LoginPtr extends IBasePresenter{
void login(String username, String password);
}
/**
* model 层接口
*/
public interface LoginMdl {
void login(String username, String password, HttpResponseListener callbak);
}
}
其实,就是将View
、Presenter
、Model
中的实现接口写在一起,看起来更加规范清晰,方便查找
- LoginModel
public class LoginMdl extends BaseModel implements LoginContacts.LoginMdl{
/**
* 登录
*
* @param username 用户名
* @param password 密码
* @param callbak 网络请求回调
*/
@Override
public void login(String username, String password, HttpResponseListener callbak) {
HashMap<String, String> map = new HashMap<>();
map.put("username", username);
map.put("password", Md5Util.encrypt(password));
RequestBody body = ReqBodyHelper.createJson(map);
// 发送网络请求
sendRequest(HttpUtils.getApi().getLoginInfo(body),callbak);
}
}
- LoginPtr
public class LoginPtr extends BasePresenter<LoginContacts.LoginUI, LoginBean> implements LoginContacts.LoginPtr, HttpResponseListener<LoginBean> {
private LoginContacts.LoginMdl mLoginMdl;
public LoginPtr(@NonNull LoginContacts.LoginUI view) {
super(view);
// 实例化 Model 层
mLoginMdl=new LoginMdl();
}
@Override
public void login(String username, String password) {
//显示进度条
showLoading();
mLoginMdl.login(username,password,this);
}
@Override
public void onSuccess(Object tag, LoginBean t) {
// 先判断是否已经与 View 建立联系
if (isViewAttach()) {
// 隐藏进度条
hideLoading();
// 登录成功调用
getView().loginSuccess();
}
}
@Override
public void onFailure(Object tag, HttpFailure failure) {
if (isViewAttach()) {
// 隐藏进度条
hideLoading();
// 登录失败调用
getView().loginFailure();
}
}
}
- LoginActivity
public class LoginActivity extends BaseActivity<LoginContacts.LoginPtr> implements LoginContacts.LoginUI {
private Button btn_login;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
btn_login=findViewById(R.id.btn_login);
btn_login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 向 Presenter 层发送登录请求
getPresenter().login("admin","123456");
}
});
}
@Override
public LoginContacts.LoginPtr onBindPresenter() {
return new LoginPtr(this);
}
@Override
public void loginSuccess() {
Toast.makeText(this,"登录成功",Toast.LENGTH_LONG).show();
}
@Override
public void loginFailure() {
Toast.makeText(this,"登录失败",Toast.LENGTH_LONG).show();
}
}
总结
MVP
是一种思想,并不是指某种固定框架,每个人都有自己独特的理解。当前市场上除了MVP
,还有MVC
、MVVM
以及他们的变种模式!特别是当MVVM
的出现,倍受开发者的推崇,可能会让人觉得MVP
已经没落!其实不然,没有哪种模式是没落的,哪怕是MVC
,也有许多大公司仍然在坚持使用,而且使用的很好!需要根据实际项目需求以及 个人对这些模式的掌控力来选择项目设计模式,不一定要用最新潮,自己用的顺,用的好,那就是适合你的设计模式!