说明:不讲原理,不讲优化,就是干
目标:学会如何搭建最最基本的mvp架构
简介
我承认画图不是我的强项
MVP是MVC衍生出来的架构,现在也比较成熟了,用的人也多了,面试也会考了,所以你必须要知道了
M:数据层(数据库,文件,网络等)
V:UI层(Activity,Fragment,View及其子类,Adapter及其子类)
P:中介(作用:关联V和M)
经过思想斗争,我觉得理论上的东西解释再多不如代码敲一遍,下面用一个用户登陆功能讲解如何搭建MVP架构,顺便简单用下Retrofit网络请求库(别怕都有注释)总之,MVP记住一个核心思想:V层就只做UI的操作,所有的业务处理和数据处理都交给P层(不然要你何用)就相当于把Activity里你写的众多网络请求和数据处理代码统统提取封装到P里了
MVP结构
api:Retrofit专用的,就两行代码,和mvp无关,待会给你看
bean:登陆的实体类,和mvp无关
login:可以看出是按功能分包。他比平时见到的多出来两个类,一个是 LoginPresenter(P层),一个是 LoginContract(契约接口类,用于将P和V接口封装到一起)他们是mvp重要的组成部分
架构图如下:
思路
mvp结构实现分三步:
一,搞一个接口契约类Contract,内含V接口和P接口
二,搞一个实现V接口的view类
三,搞一个实现P接口的presenter类
第一步:
搞一个接口契约类Contract,内含V接口和P接口。V接口里面放的是UI更新方法(V接口的实现类用到的方法);P接口里面放的是网络请求,数据读取,文件读取等具体的业务操作方法(P接口的实现类用到的方法)
从动态图可以看到,一共有三个地方有UI界面变化
①点击登陆展示等待加载遮罩
②登陆成功或者失败后取消等待加载遮罩
③取消等待加载遮罩的同时吐司
所以V接口里会有三个UI更新方法,那么就在V接口写三个方法呗,值得注意的是setPresenter方法只是用来把V层和P层关联的,本身与UI更新无关,但是必须要有的
P接口在本例中只需要实现一个登陆按钮触发的网络请求就可以啦,所以只有一个方法
由此可见,契约类Contract把V和P接口封装在了一块,而V和P接口又把具体的view和presenter用到的方法封装在了一块
/**
* 包含View和Presenter的契约接口
* Created by wangjiong on 2017/12/7.
*/
publicinterfaceLoginContract{
/**
* 与UI相关,与view相关操作
*/
interfaceView{
// 定义Presenter
voidsetPresenter();
// 展示等待加载页面
voidshowLoading();
// 隐藏等待加载页面
voidhideLoading();
// 显示登陆信息
voidshowLoginInfo(String msg);
}
/**
* 与业务相关
*/
interfacePresenter{
/**
* 登陆
* @param userId 用户id
* @param userPassword 密码
*/
voidlogin(String userId, String userPassword);
}
}
第二步:
搞一个实现V接口的view类。本例中的view类就是LoginActivity。首先实现接口所有的方法是必须的,然后这里有两个地方需要注意
一,我们是在实现V层接口setPresenter方法里通过LoginPresenter的构造方法将LoginActivity传递过去的(LoginPresenter是P的实现类),这里要记得主动调用一下setPresenter方法触发初始化presenter这个事
二,就是所谓的MVP中V层和P层分隔开,UI和业务分隔开。比如本例登陆按钮的点击事件,他并不是我们平时看到的直接撸网络请求代码,而是调用了P层里的的方法,说人话就是把网络请求一大堆代码封装了一个方法放到了P层那个类里头了,我们在调用的时候传需要用到的参数就行了。你说坑不坑,这点玩意说的那么高大尚
/**
* MVP层中的View层
*/
publicclassLoginActivityextendsAppCompatActivityimplementsLoginContract.View{
privateLoginPresenter mPresenter;
privateEditText mEtName, mEtPwd;
privateLinearLayout mLayoutLoading;
@Override
protectedvoidonCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
setPresenter();// 初始化presenter(很重要!不能说重写就不管了,一定要在view初始化调用此方法)
mEtName = findViewById(R.id.et_name);// 用户名
mEtPwd = findViewById(R.id.et_pwd);// 密码
mLayoutLoading = findViewById(R.id.layout_loading);// 遮罩层
findViewById(R.id.btn_login).setOnClickListener(newView.OnClickListener() {// 登陆按钮
@Override
publicvoidonClick(View view){// 点击登陆不是直接请求网络,而是通过presenter请求网络,然后将请求回来的数据交给view来更新
mPresenter.login(mEtName.getText().toString().trim(), mEtPwd.getText().toString().trim());
}
});
}
@Override
publicvoidsetPresenter(){
mPresenter =newLoginPresenter(this);// 一是为了实例化presenter,二是通过构造方法将view实例传递给presenter
}
@Override
publicvoidshowLoading(){// 展示遮罩层
mLayoutLoading.setVisibility(View.VISIBLE);
}
@Override
publicvoidhideLoading(){// 隐藏遮罩层
mLayoutLoading.setVisibility(View.GONE);
}
@Override
publicvoidshowLoginInfo(String msg){// 吐司登陆信息
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
}
第三步:
搞一个实现P接口的presenter类。本例中presenter类就是LoginPresenter,可以看到他就一个构造方法(用来关联V层并获取view实例化对象)和一个请求网络数据的方法。高能预警:网络请求前UI页面需要展示一个遮罩层,所以调用了view的showLoading方法,请求结束后UI页面需要取消遮罩并吐司,所以调用了view的hideLoading方法和showLoginInfo方法
/**
* MVP中的P层
* Created by wangjiong on 2017/12/7
*/
publicclassLoginPresenterimplementsLoginContract.Presenter{
privateLoginContract.View mView;
publicLoginPresenter(LoginContract.View view){// 获取到view的实例化对象
this.mView = view;
}
@Override
publicvoidlogin(String userId, String password){
mView.showLoading();// 调用view的展示遮罩方法(view用来更新具体的UI)
// 网络请求(可以自己封装一个网络库)
Retrofit retrofit =newRetrofit.Builder()
.baseUrl("http://192.168.1.101:8080/merclienttest/")// 请求的url
.addConverterFactory(GsonConverterFactory.create())// 设置Gson为实体类解析工具
.build();
LoginApi loginApi = retrofit.create(LoginApi.class);// 传入一个接口类并返回此类的实例对象
Call call = loginApi.login(userId, password);// 调用类中定义的login方法
call.enqueue(newCallback() {// retrofit异步请求
@Override
publicvoidonResponse(Call call, Response response){
mView.hideLoading();// 调用view的隐藏遮罩方法(view用来更新具体的UI)
mView.showLoginInfo(response.body().getMsg());// 调用view的吐司方法(view用来更新具体的UI)
}
@Override
publicvoidonFailure(Call call, Throwable t){
mView.hideLoading();
}
});
}
}
全剧终
妈个鸡这是啥玩意?完了?哈哈哈,没错这就是MVP。就是这么简单。只需要记住一个核心思想V层仅仅是处理UI页面的(只管化妆接客,拉皮条找老鸨子P),业务逻辑放在P层去处理(网络请求,数据库,文件等),P处理完之后再调用一下V层已经写好的对应更新UI的方法即可
扩展
没忘记Retrofit哦,简单介绍下使用方法
分三步:
一,搞一个接口类Api
二,搞一个对应的实体类bean
三,调用Retrofit方法请求网络
第一步:搞一个Api接口。本例中是LoginApi
第一行代码 @GET("login") 这个Get就代表get请求,如果换成Post那就代表post请求。login代表接口名(接口名需要后台提供)
第二行代码 Call login(@Query("userId") String userId, @Query("password") String password); 其中 login 代表自己定义的一个方法,名字随便起(小驼峰式命名),@Query("userId") 代表接口中有个名为 userId 的参数, String userId 代表给这个参数传值,值为userId(名字随便起)
/**
* 登陆接口
* Created by wangjiong on 2017/12/7.
*/
publicinterfaceLoginApi{
@GET("login")//get请求login接口(接口名需要后台提供)
Calllogin(@Query("userId") String userId, @Query("password") String password);// 声明一个login的方法(随便写),两个string形参,并返回一个实体类LoginBean
}
没反应过来?
jsonObject.put("userId",userId) 这样写懂了吧,第一个userId是接口里的参数名,第二个userId是你传的字符串值,名字随便搞的,叫啥都行
你也可以@Query("userId") String myUserId这就等价于
jsonObject.put("userId",myUserId ) 这里只是为了说明问题,所以不必太在意细节
第二步,搞一个对应的实体类bean。这个对应本例中的LoginBean。推荐用GsonFormat插件自动生成,啥?没听过,你又不是妹子,百度吧
publicclassLoginBean{
/**
* code : 200
* msg : 登陆成功
*/
privateString code;
privateString msg;
publicStringgetCode(){
returncode;
}
publicvoidsetCode(String code){
this.code = code;
}
publicStringgetMsg(){
returnmsg;
}
publicvoidsetMsg(String msg){
this.msg = msg;
}
}
第三步,调用Retrofit方法请求网络。做完上面两步,就可以正常调用了,咋用?你又不是妹子,参考LoginPresenter吧
/**
* MVP中的P层
* Created by wangjiong on 2017/12/7
*/
publicclassLoginPresenterimplementsLoginContract.Presenter{
privateLoginContract.View mView;
publicLoginPresenter(LoginContract.View view){// 获取到view的实例化对象
this.mView = view;
}
@Override
publicvoidlogin(String userId, String password){
mView.showLoading();// 调用view的展示遮罩方法(view用来更新具体的UI)
// 网络请求(可以自己封装一个网络库)
Retrofit retrofit =newRetrofit.Builder()
.baseUrl("http://192.168.1.101:8080/merclienttest/")// 请求的url
.addConverterFactory(GsonConverterFactory.create())// 设置Gson为实体类解析工具
.build();
LoginApi loginApi = retrofit.create(LoginApi.class);// 传入一个接口类并返回此类的实例对象
Call call = loginApi.login(userId, password);// 调用类中定义的login方法
call.enqueue(newCallback() {// retrofit异步请求
@Override
publicvoidonResponse(Call call, Response response){
mView.hideLoading();// 调用view的隐藏遮罩方法(view用来更新具体的UI)
mView.showLoginInfo(response.body().getMsg());// 调用view的吐司方法(view用来更新具体的UI)
}
@Override
publicvoidonFailure(Call call, Throwable t){
mView.hideLoading();
}
});
}
}
源码地址:https://github.com/GodJiong/mvp
网友评论