Android MVP应用
Android 谷歌官方MVP模式解读
一. 目录
二.MVP的含义
MVP在我上一个写关于地图的项目时候,在项目开始前也进行过学习,也在项目中也进行了应用。在今年暑假开始写西邮助手的时候,和小伙伴也是计划用MVP打框架。重新查阅资料发现,谷歌官方提供了一个MVP模式的Demo。所以对它进行了学习阅读,同时决定写一篇博客进行记录。
1.MVC模式
MVC即Model-View-Controller.即M:逻辑模型,V:视图模型,C:控制器
MVC模式下,系统框架的类库被划分为3种:模型(Model)、视图(View)、控制器(Controller)。模型对象负责建立数据结构和相应的行为操作处理。视图对象负责在屏幕上渲染出相应的图形信息展示给用户看。控制器对象负责截获用户的按键和屏幕触摸等事件,协调Model对象和View对象。
用户与视图交互,视图接收并反馈用户动作,视图把用户的请求传给相应的控制器,由控制器决定调用哪个模型,然后由模型调用相应的业务逻辑对用户请求进行加工处理,最后由模型对视图进行选择。即如下图所示
Android中的MVC
Android世界中也经常运用到MVC模式。
Activity对应视图界面也就是View层。
数据库文件,Sharedprefrence,内存缓冲,磁盘缓冲等数据内容对应Model层。
而Controller控制层基本上也由Activity层面来进行。
MVC中的不足
是的MVC是挺好的,但是它也有它的缺点,特别是针对Androi开发。
因为Android的特殊性,使得Activity对应了MVC中的V和C,同时担任两个角色,就有了类似“既当爹又当妈”的感觉,这显然就不符合软件设计原则的“单一职责”原则。但现实中是很多的APP代码中有这么的处境,特别是Androi原生的很多系统APK,某些Activity动则几千行代码。 所以越来越多的人选择MVP模式。
2.MVP
MVP即:Model-View-Presenter,MVP是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。
MVP和MVC最大的区别就是 moder不能直接调用View,他们之间的通信是通过Presenter来进行的,所有的交互都发生在Presenter内部。两这区别如下:
在Android中应用到MVP,就是可以是V和P之间进行相互通信,P和M之间进行相互通信。简单举个下拉刷新的例子。
下拉刷新: V(刷新)–>P(通知M进行网络请求)–>M(进行网络请求);M(请求完毕)—>P(通知V加载更多)—>V(加载数据)。
三.原来的写法
我原来的写法是参考这一篇博客的
Android中MVP模式讲解及实践
1. 定义View接口,里面供提供p层抽象方法
public interface IWetherView {
public void onInfoUpdate(String info);
public void showWaitingDialog();
public void dissmissWaitingDialog();
}
2. Activity或者Fragemt实现View接口
public class MainActivity extends AppCompatActivity implements IWetherView{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void onInfoUpdate(String info) {
}
@Override
public void showWaitingDialog() {
}
@Override
public void dissmissWaitingDialog() {
}
}
3. Model层的接口定义,里面供提供p层抽象方法
public interface IWetherModel {
//提供数据
public String getInfo();
//存储数据
public void setInfo(String info);
}
4. Model层实现
public class IWetherImpl implements IWetherModel {
@Override
public String getInfo() {
return null;
}
@Override
public void setInfo(String info) {
}
}
5.Presenter代码及实现
Presenter中实现对V和M的调用,所以在P中要持有V和M的对象
public interface IWetherModel {
IWetherModel mModel;
IWetherView mView;
//供View层调用,用来请求天气数据
public void requestWetherInfo(){
}
}
6.Presenter与View的通信
View—–>Presenter
public class MainActivity extends AppCompatActivity implements IWetherView{
......
WetherPresenter mPresenter =WetherPresenter(this) ;
......
}
Presenter—>View
public class WetherPresenter {
IWetherModel mModel;
IWetherView mView;
......
public WetherPresenter(IWetherView mView) {
this.mView = mView;
}
......
}
7.Presenter与View的通信
Presenter—->Model
public class WetherPresenter {
IWetherModel mModel;
IWetherView mView;
......
public WetherPresenter(IWetherView mView) {
this.mView = mView;
mModel = new IWetherModelImpl(this);
}
......
}
Model—->Presenter
public class IWetherImpl implements IWetherModel {
WetherPresenter mPresenter;
public IWetherImpl(WetherPresenter presenter) {
this.mPresenter = presenter;
}
@Override
public String getInfo() {
return null;
}
@Override
public void setInfo(String info) {
}
}
完成这些后,就可以进行V–>P–>M–>P–>V 的过程
四.借鉴谷歌官方的写法
我借鉴的是谷歌MVP-Rxjava的写法,大部分和原写法相同,在某些地方进行了改进,谷歌官方的写法中,Fragment作为V,Activity只是作为将两者进行绑定的渠道,我在项目中,将Activity作为V。
谷歌官方MVP + Rxjava的讲解:Google官方MVP+Rxjava项目详解
在我的项目中的应用:以四六级查询为例
1.定义基础的P和V接口
public interface BasePresenter {
//订阅
void subscribe();
//取消订阅
void unSubscribe();
}
public interface BaseView<T> {
void setPresenter(T presenter);
}
2.定义Contract接口
在Contract接口中设置该模块 V和P提供的接口
public interface ComputersGradeContract {
interface View extends BaseView<Presenter> {
void cgGetTimes(CgTimes cgTimes); //展示时间
void cgShowValidateCode(Bitmap bitmap); //展示验证码
void cgShowGrade(CgQuery cgQuery); //展示成绩
void cgFailure(String str); //请求失败
}
interface Presenter extends BasePresenter {
void cgSetValidateCode(); //请求验证码
void cgSetGrade(CgDemandDate cgDemandDate); //请求成绩
}
}
3.定义协议类EducationDataSource
这个就是定义M的接口,里面写了M需要实现的接口。
public interface EducationDataSource {
//计算机等级考试
Observable<CgTimes> cgTimes();
Observable<Bitmap> cgValidateCode();
Observable<CgQuery> cgGrade(CgDemandDate cgDemandDate);
}
4.V的实现类 ComputersGradeActivity
public class ComputersGradeActivity extends BaseActivity implements ComputersGradeContract.View {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_computers_grade);
initView();
clickJudge();
//创建一个P实例
//参数1 M的实例
//参数2 V的实例
new ComputersGradePresenter(Injection.provideTasksRepository(getApplicationContext()), this);
}
...
@Override
public void onResume() {
super.onResume();
mPresenter.subscribe(); //P进行订阅
}
@Override
public void onPause() {
super.onPause();
mPresenter.unSubscribe();//P取消订阅
}
@Override
public void setPresenter(@NonNull ComputersGradeContract.Presenter presenter) {
mPresenter = presenter; // v-->P
}
@Override //展示时间 提供给P调用的接口
public void cgGetTimes(CgTimes cgTimes) {
mDateSet = cgTimes.data;
OptionsPickerView lessonDatePickerView = getDateOptionPickerView(cgTimes);
mLlDate.setOnClickListener(view -> lessonDatePickerView.show());
}
@Override //展示验证码 提供给P调用的接口
public void cgShowValidateCode(Bitmap bitmap) {
mIvCode.setImageBitmap(bitmap);
}
@Override//展示成绩 提供给P调用的接口
public void cgShowGrade(CgQuery cgQuery) {
if(cgQuery.status == 500){
Toast.makeText(Utils.getContext(), "信息有误", Toast.LENGTH_SHORT).show();
}else if(cgQuery.data == null){
Toast.makeText(Utils.getContext(), "验证码错误", Toast.LENGTH_SHORT).show();
mPresenter.cgSetValidateCode();
}else {
mAnimatorSet.start();
mTvGrade.setText(cgQuery.data.status);
mTvCredential.setText(cgQuery.data.zkzh);
}
}
@Override //请求失败
public void cgFailure(String str) {
Toast.makeText(Utils.getContext(), str, Toast.LENGTH_SHORT).show();
}
}
Injection类
public class Injection {
public static EducationRepository provideTasksRepository(@NonNull Context context) {
return EducationRepository.getInstance(
EducationRemoteDataSource.getInstance(),
EducationLocalDataSource.getInstance(context));
}
}
5. P的实现类 ComputersGradePresenter
public class ComputersGradePresenter implements ComputersGradeContract.Presenter {
@NonNull
private final ComputersGradeContract.View mView; //V的实例
@NonNull
private final EducationRepository mEducationRepository; //M的实例
@NonNull
private CompositeDisposable mCompositeDisposable;
private static final String TAG = "ComputersGradePresenter";
public ComputersGradePresenter(@Nullable EducationRepository educationRepository,
@NonNull ComputersGradeContract.View view) {
//p和V绑定
mView = view;
//p和M绑定
mEducationRepository = educationRepository;
//V和p绑定
mView.setPresenter(this);
mCompositeDisposable = new CompositeDisposable();
}
//订阅
@Override
public void subscribe() {
cgSetTimes(); //请求时间
cgSetValidateCode(); //请求验证码
}
//取消订阅
@Override
public void unSubscribe() {
mCompositeDisposable.clear();
}
//获取时间列表
public void cgSetTimes() { //使用RXjava对M发送回的事件进行处理
mCompositeDisposable.add(mEducationRepository
.cgTimes() //调用M的cgTimes方法
.subscribeOn(Schedulers.io()) //提供Scheduler用于Rxjava线程调度
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
// onNext
cgTimes-> {
if (cgTimes == null) {
mView.cgFailure("网络请求失败"); //调用页面出错
} else {
mView.cgGetTimes(cgTimes);
}
},
// onError
throwable -> mView.cgFailure("网络请求失败")));//调用页面出错
}
@Override //获取验证码
public void cgSetValidateCode() {
mCompositeDisposable.add(mEducationRepository
.cgValidateCode()
.subscribeOn(Schedulers.io()) //提供Scheduler用于Rxjava线程调度
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
// onNext
bitmap-> {
if (bitmap == null) {
mView.cgFailure("网络请求失败1"); //调用页面出错
} else {
mView.cgShowValidateCode(bitmap);
}
},
// onError
throwable -> mView.cgFailure("网络请求失败2"))); //调用页面出错
}
@Override //获取成绩
public void cgSetGrade(CgDemandDate cgDemandDate) {
mCompositeDisposable.add(mEducationRepository
.cgGrade(cgDemandDate)
.subscribeOn(Schedulers.io()) //提供Scheduler用于Rxjava线程调度
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
// onNext
cgQuery-> {
if (cgQuery == null) {
mView.cgFailure("信息有误"); //调用页面出错
} else {
mView.cgShowGrade(cgQuery);
}
},
// onError
throwable -> mView.cgFailure("信息有误"))); //调用页面出错
}
}
6. M的实现类
M的实现类有三个:EducationRepository,EducationRemoteDataSource代表网络数据,EducationLocalDataSource 代表本地数据,EducationRepository根据需求决定调用哪一个DataSource。
EducationRepository
ublic class EducationRepository implements EducationDataSource {
@NonNull
private final EducationDataSource mTasksRemoteDataSource;
@NonNull
private final EducationDataSource mTasksLocalDataSource;
//单例模式
private EducationRepository(@NonNull EducationDataSource tasksRemoteDataSource,
@NonNull EducationDataSource tasksLocalDataSource) {
mTasksRemoteDataSource = tasksRemoteDataSource;
mTasksLocalDataSource = tasksLocalDataSource;
}
@Nullable
private static EducationRepository INSTANCE = null;
public static EducationRepository getInstance(@NonNull EducationDataSource tasksRemoteDataSource,
@NonNull EducationDataSource tasksLocalDataSource) {
if (INSTANCE == null) {
INSTANCE = new EducationRepository(tasksRemoteDataSource, tasksLocalDataSource);
}
return INSTANCE;
}
@Override
public Observable<CgTimes> cgTimes() {
return mTasksRemoteDataSource.cgTimes();
}
@Override
public Observable<Bitmap> cgValidateCode() {
return mTasksRemoteDataSource.cgValidateCode();
}
@Override
public Observable<CgQuery> cgGrade(CgDemandDate cgDemandDate) {
return mTasksRemoteDataSource. cgGrade(cgDemandDate);
}
}
EducationRemoteDataSource类
public class EducationRemoteDataSource implements EducationDataSource {
private static EducationRemoteDataSource INSTANCE; //单例
// Prevent direct instantiation.
private EducationRemoteDataSource() {
}
public static EducationRemoteDataSource getInstance() {
if (INSTANCE == null) {
INSTANCE = new EducationRemoteDataSource();
}
return INSTANCE;
}
@Override
public Observable<CgTimes> cgTimes() {
return RetrofitFactory.INSTANCE.create(EducationApi.class)
.cgGetTimes()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.flatMap(new Function<CgTimes, ObservableSource<CgTimes>>() {//将请求的结果封装成一个新的事件
@Override
public ObservableSource<CgTimes> apply(CgTimes cgTimes) throws Exception {
return Observable.create(e -> e.onNext(cgTimes));
}
});
}
@Override
public Observable<Bitmap> cgValidateCode() {
return RetrofitFactory.INSTANCE.create(EducationApi.class)
.cgGetCode()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.flatMap(new Function<ResponseBody, ObservableSource<Bitmap>>() { //将请求的结果封装成一个新的事件
@Override
public ObservableSource<Bitmap> apply(ResponseBody responseBody) throws Exception {
Bitmap bitmap = BitmapFactory.decodeStream(responseBody.byteStream());
return Observable.create(e -> e.onNext(bitmap));
}
});
}
@Override
public Observable<CgQuery> cgGrade(CgDemandDate cgDemandDate) {
return RetrofitFactory.INSTANCE.create(EducationApi.class)
.cgGetQuery(cgDemandDate.getDate(),cgDemandDate.getType(),cgDemandDate.getNum(),cgDemandDate.getName(),cgDemandDate.getCode())
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.flatMap(new Function<CgQuery, ObservableSource<CgQuery>>() {
@Override
public ObservableSource<CgQuery> apply(CgQuery cgGuery) throws Exception {
return Observable.create(e -> e.onNext(cgGuery));
}
});
}
在我的项目中,这个模块没有用的EducationLocalDataSource,所以基本它对应的方法返回的都是空值。
下一篇: MVP andorid 使用