MVP+Retrofit2+RxJava2
本Demo使用MVP+Retrofit2+RxJava2来写的,如果大家对Retrofit2+RxJava都不会使用进行网络请求的话,请先查看我之前写的文章Retrofit2.0+RxJava2.0封装使用,因为我这里面网络请求是直接使用之前封装好的,这里就不再介绍了。这里只介绍Android MVP的简单使用和封装使用。
Demo地址:https://github.com/pengjunshan/MVPRetrofitRxJava
其它文章
Retrofit2+RxJava2 封装使用
OkHttp3简单使用和封装使用
Android开发 多语言、指纹登录、手势登录
Android使用IconFont阿里矢量图标
Android Studio 使用SVN 主干和分支合并代码
主要讲解内容
- MVP封装前简单使用
- MVP对Activity封装使用
- MVP对Frgment封装使用
效果图
MVP介绍
MVP全名Mode View Presenter,Presenter处理逻辑业务,Model提供数据,View更新展示界面。完全隔离界面显示与业务逻辑。
优点:
- 分离了视图逻辑和业务逻辑,降低了耦合。
- 单一职责, Model, View, Presenter只处理单一逻辑。
- Model层的修改和View层的修改互不影响。
- 视图逻辑和业务逻辑分别抽象到了View和Presenter的接口中去,提高代码的可阅读性
缺点:
- 接口类过多,代码量增加。
- Presenter和View相互持有引用,解除不及时的话容易出现内存泄漏。
说明:
- 这里对MVP不做过多的解释,网上对MVP的解释有一大堆基本说的都差不多。
- 下面我会讲解怎么尽可能的减少代码量,和避免内存泄漏的方法。
- MVP并没有一个统一标准模式,只要遵循View和Model相互分离就行。(按照自己喜欢的模式写就行)
MVP简单使用方法
用到的类
Concacts:契约类 将Model、View、Presenter 进行约束管理,方便后期类的查找、维护。
View:Activity 和Fragment 视为View层,负责处理 UI。
Model:包含着具体的数据请求,数据源、本地存储等等。
Presenter :为业务处理层,既能调用View逻辑,又能调用Model请求数据。
EaseConcacts 类
契约类 将Model、View、Presenter 进行约束管理。在各自的接口类中定义不同需求的方法。IView接口类中创建一个接收Data数据的回调方法;IPresenter接口类中定义一个IView触发网络请求的方法;IModel接口类中定义一个真正网络请求的方法和一个回调给IPresenter类的回调方法;
public class EaseConcacts {
interface IView {
/**
* View层获取数据回调方法
*/
void onResultData(String data);
}
interface IPresenter {
/**
* View层向Presenter发送请求方法
*/
void requestData(Context context);
}
interface IModel {
/**
* Persenter层向Model发送请求方法
*/
void getData(Context context, ModelListener modelListener);
interface ModelListener {
/**
* Model层请求接口完成后回调Persenter层方法
*/
void onReslutJson(String test);
}
}
}
EaseActivity类
IView类中实现Concacts中的IView接口,并且初始化Presenter,然后点击按钮触发Presenter类中的requestData方法;requestData方法中会触发IModel中真正网络请求方法;然后IView类通过onResultData回调方法接收数据;
public class EaseActivity extends AppCompatActivity implements IView {
private EasePresenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ease);
presenter = new EasePresenter(this);
findViewById(R.id.getJSON).setOnClickListener((view) -> presenter.requestData(this));
}
@Override
public void onResultData(String data) {
Toast.makeText(this, data, Toast.LENGTH_SHORT).show();
}
}
EasePresenter类
首先要实现EaseConcacts契约类中的IPresenter接口,并实现requestData方法,当View调用requestData方法后Presenter会通过Mode进行网络请求,然后把结果回调过来。
public class EasePresenter implements EaseConcacts.IPresenter {
private EaseConcacts.IView iView;
private EaseModel iModle;
public EasePresenter(EaseConcacts.IView view) {
this.iView = view;
this.iModle = new EaseModel();
}
@Override
public void requestData(Context context) {
iModle.getData(context, test -> iView.onResultData(test));
}
EaseModel类
首先要实现EaseConcacts契约类中IModel接口,这里写网络业务代码,网络请求完成后可以在此可以处理数据 增删改查、本地存储等等;然后把数据通过接口返回给EasePresenter类;
public class EaseModel implements EaseConcacts.IModel {
@Override
public void getData(Context context, ModelListener modelListener) {
RetrofitClient.request(context, RetrofitClient.createApi().getJson(),
new IResponseListener<BannerBean>() {
@Override
public void onSuccess(BannerBean data) {
/**
* 在此可以处理数据 增删改查、本地存储等等
*/
Logout.e("data= "+data.toString());
modelListener.onReslutJson(data.toString());
}
@Override
public void onFail(OkHttpException failuer) {
modelListener.onReslutJson("失败= "+failuer.getEmsg());
}
});
}
}
Model弱化
可以弱化Model的作用,把RetrofitClient网络请求当做Modle,可以省略Modle接口和类文件
public class EasePresenter implements EaseConcacts.IPresenter {
private EaseConcacts.IView iView;
private EaseModel iModle;
public EasePresenter(EaseConcacts.IView view) {
this.iView = view;
this.iModle = new EaseModel();
}
@Override
public void requestData(Context context) {
/**
* 方式一:调用Modle进行调用接口
*/
//iModle.getData(context, test -> iView.onResultData(test));
/**
* 方式二:弱化了Model的作用,这里RetrofitClient网络请求就是Modle,可以省略一个Modle文件
*/
RetrofitClient.request(context, RetrofitClient.createApi().getJson(),
new IResponseListener<BannerBean>() {
@Override
public void onSuccess(BannerBean data) {
Logout.e("data= " + data.toString());
iView.onResultData(data.toString());
}
@Override
public void onFail(OkHttpException failuer) {
iView.onResultData("失败= " + failuer.getEmsg());
}
});
}
}
封装MVP使用
上面说过MVP缺点,Presenter和View相互持有引用,解除不及时的话容易出现内存泄漏。下面我们要创建一些Base类来处理一些公共的代码逻辑和防止内存泄漏。
创建Base类
BaseActivity:通过泛型接收View和Presenter,进行V和P的绑定和解绑触发。
BaseFragment:通过泛型接收View和Presenter,进行V和P的绑定和解绑触发。
BasePresenter:动态判断View是否已销毁,防止内存泄漏。
BaseView:为了处理公共View逻辑。比如在BaseView中写公共处理UI方法、通过泛型对View进行绑定和解绑。
BaseView类
在BaseActivity、BaseFragment、BasePresenter中都要处理View的逻辑,都需要用泛型来接收View,所以所有的IView都要继承BaseView。
public interface BaseView {
//添加公共处理UI方法 比如处理接口失败的UI
}
BasePresenter类
所有的Presenter类都要继承BasePresenter,在Base里对View做了统一的绑定和解绑的代码处理。还统一对View做了是否已解绑的判断,防止调用View中的回调方法(防止内存泄漏)。
public abstract class BasePresenter<V extends BaseView> {
private V view;
private V proxyView;
public V getView() {
return proxyView;
}
/**
* 绑定view
*/
public void attachView(V view) {
this.view = view;
//参数一:类加载器
ClassLoader classLoader = view.getClass().getClassLoader();
//参数二:代理接口
Class<?>[] interfaces = view.getClass().getInterfaces();
//参数三:方法回调
BaseViewInvocationHandler handler = new BaseViewInvocationHandler(view);
proxyView = (V) Proxy.newProxyInstance(classLoader, interfaces, handler);
}
/**
* 解绑view
*/
public void detachView() {
this.view = null;
}
private class BaseViewInvocationHandler implements InvocationHandler {
private BaseView view;
BaseViewInvocationHandler(BaseView view) {
this.view = view;
}
//统一判断->控制对象访问权限
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
//检查是不是为null
if (isViewNull()) {
//不用回调
return null;
}
//执行回调
return method.invoke(view, objects);
}
}
private boolean isViewNull() {
if (view == null) {
return true;
}
return false;
}
}
BaseActivity类
通过泛型接收View和Presenter,进行V和P的绑定和解绑触发。还有提供创建Presenter类的抽象方法,和获取Presenter类的构造方法。
public abstract class BaseActivity<V extends BaseView, P extends BasePresenter<V>> extends
Activity implements BaseView {
private P presenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (presenter == null) {
presenter = createPresenter();
}
/**
* 绑定view
*/
if (presenter != null) {
presenter.attachView((V) this);
}
}
/**
* 得到当前的Presenter类
*/
public P getPresenter() {
return presenter;
}
/**
* 创建Presenter类
*/
public abstract P createPresenter();
@Override
protected void onDestroy() {
super.onDestroy();
/**
* 解绑view
*/
if (presenter != null) {
presenter.detachView();
}
}
}
BaseFragment类
通过泛型接收View和Presenter,进行V和P的绑定和解绑触发。还有提供创建Presenter类的抽象方法,和获取Presenter类的构造方法。和BaseActivity处理方式是一样的。
public abstract class BaseFragment<V extends BaseView, P extends BasePresenter<V>> extends
Fragment implements BaseView {
private P presenter;
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (presenter == null) {
presenter = createPresenter();
}
if (presenter != null) {
presenter.attachView((V) this);
}
}
public P getPresenter() {
return presenter;
}
public abstract P createPresenter();
@Override
public void onDestroyView() {
super.onDestroyView();
if (presenter != null) {
presenter.detachView();
}
}
}
Activity中使用
LoginConcacts契约类
来统一管理IView、IPresenter、IModel接口。可以对Model进行弱化,不仅少定义了IModel接口还可以少写一个实现IModel接口类,从而减少了代码量。
public class LoginConcacts {
interface IView extends BaseView {
/**
* 校验账号密码错误的提示信息
*/
void showToast(String msg);
/**
* View中获取接口信息的回调
*/
void onResultData(String data);
}
interface IPresenter {
/**
* 校验账号密码
*/
void checkData(String userName, String userPwd);
/**
* 请求数据
*/
void requestData(String userName, String userPwd);
}
/**
* Model可以弱化掉,节省代码量
*/
interface IModel {
void getData(Context context, String userName, String userPwd, ModelListener modelListener);
interface ModelListener {
void onReslutJson(String test);
}
}
}
LoginPresenter类
首先要继承BasePresenter,通过泛型传入LoginConcacts.IView,再实现LoginConcacts.IPresenter接口类并实现里面的方法。在LoginPresenter里可以直接进行网络请求,把Retrofit网络请求当做Model(把Model弱化),也可以调用LoginModel进行网络请求。
public class LoginPresenter extends BasePresenter<LoginConcacts.IView> implements
LoginConcacts.IPresenter {
private Context context;
private LoginModel modle;
public LoginPresenter(Context context) {
this.context = context;
this.modle = new LoginModel();
}
@Override
public void checkData(String userName, String userPwd) {
if (TextUtils.isEmpty(userName)) {
getView().showToast("请输入账号!");
} else if (TextUtils.isEmpty(userPwd)) {
getView().showToast("请输入密码!");
} else {
requestData(userName, userPwd);
}
}
@Override
public void requestData(String userName, String userPwd) {
/**
* 方式一:调用Modle进行调用接口
*/
// modle.getData(context,userName,userPwd, test -> getView().onResultData(test));
/**
* 方式二:把Modle弱引用掉,这里RetrofitClient网络请求就是Modle,可以省略一个Modle文件
*/
Map<String, String> map = new HashMap<>();
map.put("username", userName);
map.put("password", userPwd);
RetrofitClient.request(context, RetrofitClient.createApi().postLogin(map),
new IResponseListener<LoginBean>() {
@Override
public void onSuccess(LoginBean data) {
Logout.e("data= " + data.toString());
getView().onResultData(data.toString());
}
@Override
public void onFail(OkHttpException failuer) {
getView().onResultData("失败= " + failuer.getEmsg());
}
});
}
}
LoginModel类
首先要实现IModel接口类并实现方法,Model中不光是进行网络请求的,其它业务逻辑都可以处理。这里是进行网络请求,然后通过接口返回给Presenter。
public class LoginModel implements IModel {
@Override
public void getData(Context context, String userName, String userPwd,
ModelListener modelListener) {
Map<String, String> map = new HashMap<>();
map.put("username", userName);
map.put("password", userPwd);
RetrofitClient.request(context, RetrofitClient.createApi().postLogin(map),
new IResponseListener<LoginBean>() {
@Override
public void onSuccess(LoginBean data) {
Logout.e("data= " + data.toString());
modelListener.onReslutJson(data.toString());
}
@Override
public void onFail(OkHttpException failuer) {
modelListener.onReslutJson("失败= " + failuer.getEmsg());
}
});
}
}
LoginActivity类
LoginActivity就是View的实现类,首先要继承BaseActivity类并通过泛型传入View和Presenter。然后实现LoginConcacts.IView接口并实现其中的方法。
public class LoginActivity extends
BaseActivity<LoginConcacts.IView, LoginPresenter> implements LoginConcacts.IView {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
findViewById(R.id.getJSON)
.setOnClickListener((view) -> getPresenter().checkData("账号", "密码"));
}
@Override
public LoginPresenter createPresenter() {
return new LoginPresenter(this);
}
@Override
public void showToast(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
@Override
public void onResultData(String data) {
Toast.makeText(this, data, Toast.LENGTH_SHORT).show();
}
}
Fragment中使用
BannerConcacts类
来统一管理IView、IPresenter的接口,这里我对Model弱化掉了不再提供Model的实现类了。
public class BannerConcacts {
interface IView extends BaseView {
/**
* 获取接口数据回调方法
*/
void onReslutData(String data);
}
interface IPresenter {
/**
* 进行网络业务处理
*/
void requestData(Context context);
}
}
BannerPresenter类
首先继承BasePresenter类通过泛型传入相应的IView,然后再实现BannerConcacts.IPresenter接口并实现其中的方法。这里没有调用Model来进行业务处理,对Model弱化了
public class BannerPresenter extends BasePresenter<BannerConcacts.IView> implements
BannerConcacts.IPresenter {
@Override
public void requestData(Context context) {
RetrofitClient.request(context, RetrofitClient.createApi().getJson(),
new IResponseListener<BannerBean>() {
@Override
public void onSuccess(BannerBean data) {
Logout.e("Tag", "data= " + data.toString());
getView().onReslutData(data.toString());
}
@Override
public void onFail(OkHttpException failuer) {
getView().onReslutData("失败= " + failuer.getEmsg());
}
});
}
}
BannerFragment类
BannerFragment就是View的实现类,首先要继承BaseFragment类并通过泛型传入View和Presenter。然后实现BannerConcacts.IView接口并实现其中的方法。使用方式和Activity一样。
public class BannerFragment extends BaseFragment<BannerConcacts.IView, BannerPresenter> implements
BannerConcacts.IView {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.banner, null);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.findViewById(R.id.getJSON)
.setOnClickListener((v) -> getPresenter().requestData(getContext()));
}
@Override
public BannerPresenter createPresenter() {
return new BannerPresenter();
}
@Override
public void onReslutData(String data) {
Toast.makeText(getContext(), data, Toast.LENGTH_SHORT).show();
}
}
我对MVP的理解
有些人说MVP适合大型项目,我不这么认为。之前我做过某网的项目使用MVC模式,有些Activity代码行能达到两千多行代码,查找问题很麻烦。我说下我的看法,我认为View层业务逻辑比较多的话就适合MVP模式,业务逻辑比较少的话看个人你可以用MVP也可以用MVC。还有就是封装一个适合自己项目的框架。