todo‑mvp‑clean是Google的Android Architecture Blueprints(Android架构蓝图)中的一个用MVP架构和Clean架构来构架APP的demo,这篇文章主要是看完官方demo以后,谈谈对Android中使用MVP+Clean的理解
浅谈Android架构——MVP:https://www.jianshu.com/p/9050e3dbe513
对mvp-clean的理解
todo-mvp-clean架构模式是基于标准MVP架构和Clean Architecture的概念相结合来设计实现的。
- Presentation Layer:其实就是MVP架构下的V层和P层
- Domain Layer:抽象出来的业务逻辑,每个Use Case(用例)代表一个业务,通过Use Case实现P层和M层的通信
- Data Layer:数据操作实现层,其实就是MVP架构下的M层
其实对比MVP架构,View层和Medle层是没有改变的,只是多了Domain Layer这层,是Presenter层和Data层之间通信的桥梁,并且对数据进行了业务处理;这一层是通过一个个Use Case组成的,代表每一个业务逻辑,更方便代码的复用和维护;数据操作的细节由Data层实现;从而进一步将Presenter层和Data层解耦。
对比MVP架构,我能理解的就是把P层的业务逻辑转移到了Domain Layer中,而且每一个业务逻辑都可以新建一个Use Case,细化了业务,而且提高了代码的重用性;
具体实现
Google的demo看着打一遍差不多就能理解意思了,但是项目用了Dagger注入框架和其他测试框架,看起来可能比较麻烦,就自己写了一个简单的,便于理解;栗子还是和上篇MVP框架一样,只是新增了一个Domain Layer层,所以只展示其创建过程和其他改变;功能:
- 选择图片保存图片地址到数据库
- 获取数据库中的图片展示
- 删除数据库中的图片;
可以看出新增了几个类,首先是UseCase和UseCaseHandler,就是对业务逻辑的一个抽象和管理;UseCase抽象出请求参数、返回参数、数据返回回调接口
public abstract class UseCase {
//请求参数
private Q mRequestValues;
//返回监听
private UseCaseCallback mUseCaseCallback;
//执行业务
void run() {
executeUseCase(mRequestValues);
}
//执行的具体方法
protected abstract void executeUseCase(Q requestValues);
/**
* 请求参数
*/
public interface RequestValues {
}
/**
* 返回参数
*/
public interface ResponseValue {
}
//返回回调
public interface UseCaseCallback {
void onSuccess(R response);
void onError();
}
}
UseCaseHandler是对UseCase进行管理,使用到UseCaseScheduler和UseCaseThreadPoolScheduler这两个类来进行线程的调度,UseCaseScheduler抽象出方法,UseCaseThreadPoolScheduler实现方法,在线程池中执行异步任务;任务的结果回调用Handler post的方式来切换到主线程
对UseCaseHandler进行分析,其实就两个部分:执行任务、返回结果,还有使用了线程调度来管理整个过程
处理结果
在主线程中处理结果和异常方法
/**
* 返回数据
*
* @param response
* @param useCaseCallback
* @param
*/
public void notifyResponse(final V response,
final UseCase.UseCaseCallback useCaseCallback) {
mUseCaseScheduler.notifyResponse(response, useCaseCallback);
}
/**
* 返回错误
*
* @param useCaseCallback
* @param
*/
private void notifyError(final UseCase.UseCaseCallback useCaseCallback) {
mUseCaseScheduler.onError(useCaseCallback);
}
执行业务方法
传入请求参数和回调参数,并给UseCase设置回调UiCallbackWrapper,然后在线程池中执行具体的业务方法
public void execute(
final UseCase useCase, T values, UseCase.UseCaseCallback callback) {
useCase.setRequestValues(values);
useCase.setUseCaseCallback(new UiCallbackWrapper(callback, this));
mUseCaseScheduler.execute(new Runnable() {
@Override
public void run() {
useCase.run();
}
});
}
UiCallbackWrapper
继承UseCase.UseCaseCallback,目的是执行完业务后,UseCase执行回调就会调用UseCaseHandler的两个在主线程中处理结果的方法
private static final class UiCallbackWrapper implements UseCase.UseCaseCallback {
private final UseCase.UseCaseCallback mCallback;
private final UseCaseHandler mUseCaseHandler;
public UiCallbackWrapper(UseCase.UseCaseCallback callback, UseCaseHandler useCaseHandler) {
mCallback = callback;
mUseCaseHandler = useCaseHandler;
}
@Override
public void onSuccess(V response) {
mUseCaseHandler.notifyResponse(response, mCallback);
}
@Override
public void onError() {
mUseCaseHandler.notifyError(mCallback);
}
}
主要的东西就在这个类里面,其实就是使用了命令模式,对UseCase进行了统一的管理,维护了一个UseCaseThreadPoolScheduler 对象来执行异步任务并在UI线程返回结果
具体使用
创建UseCase
每个业务就应该新建一个UseCase,按照Clean的原则,UseCase是业务逻辑层,是由纯Java来实现的,不应该有Android库的依赖;保存图片地址到数据库为例,新建一个AddPicture用例继承UseCase
public class AddPicture extends UseCase
实现请求参数接口
用一个内部类实现请求参数接口,保存图片,图片是一个File对象
public static final class RequestValues implements UseCase.RequestValues {
File pictureFile;
public RequestValues(File pictureFile) {
this.pictureFile = pictureFile;
}
public File getPictureFile() {
return pictureFile;
}
}
实现返回参数接口
返回数据是一个定义的Picture对象,用于更新界面;
public static final class ResponseValue implements UseCase.ResponseValue {
Picture mPicture;
public ResponseValue(Picture picture) {
mPicture = picture;
}
public Picture getPicture() {
return mPicture;
}
}
实现执行方法
执行方法其实就是在数据库中插入一条数据
@Override
protected void executeUseCase(RequestValues requestValues) {
Picture picture = new Picture(requestValues.getPictureFile());
mLocalDataSource.savePic(picture);
getUseCaseCallback().onSuccess(new ResponseValue(picture));
}
在Persenter中实现数据获取
P层直接调用mUseCaseHandler
执行业务方法,获取到数据给View更新界面
@Override
public void addPic(File file) {
AddPicture.RequestValues requestValues=new AddPicture.RequestValues(file);
mUseCaseHandler.execute(mAddPicture, requestValues, new UseCase.UseCaseCallback() {
@Override
public void onSuccess(AddPicture.ResponseValue response) {
mPicturesActivity.addPic(response.getPicture());
}
@Override
public void onError() {
}
});
}
以选择图片,保存到数据库这个功能来看,调用系统相册选择图片这一步属于数据的获取,但更是界面操作,之前MVP,我封装了图片选择工具,把图片选择写在了P层,现在P层不写业务逻辑,UseCase是纯Java的业务逻辑,所以其实应该是写在View层;
执行过程分析
首先在View层选择图片以后,调动Persenter方法
PicturePickUtil.pick(PicturesActivity.this, new OnPickListener() {
@Override
public void pickPicture(File file) {
mPresenter.addPic(file);
}
});
在Presenter里面,根据File构建出请求参数,创建AddPicture对象,执行mUseCaseHandler.execute()
方法;
//创建用例
mAddPicture=new AddPicture(mLocalDataSource);
@Override
public void addPic(File file) {
AddPicture.RequestValues requestValues=new AddPicture.RequestValues(file);
mUseCaseHandler.execute(mAddPicture, requestValues, new UseCase.UseCaseCallback() {
@Override
public void onSuccess(AddPicture.ResponseValue response) {
mPicturesActivity.addPic(response.getPicture());
}
@Override
public void onError() {
}
});
}
mUseCaseHandler.execute()
的方法就是给mAddPicture
设置了请求参数和返回回调,并异步执行mAddPicture.run()
方法
public void execute(
final UseCase useCase, T values, UseCase.UseCaseCallback callback) {
useCase.setRequestValues(values);
useCase.setUseCaseCallback(new UiCallbackWrapper(callback, this));
mUseCaseScheduler.execute(new Runnable() {
@Override
public void run() {
useCase.run();
}
});
}
mAddPicture.run()
方法执行了mAddPicture.executeUseCase()
方法,也就是保存数据,并且生成返回数据执行getUseCaseCallback().onSuccess()
回调方法;
@Override
protected void executeUseCase(RequestValues requestValues) {
Picture picture = new Picture(requestValues.getPictureFile());
mLocalDataSource.savePic(picture);
getUseCaseCallback().onSuccess(new ResponseValue(picture));
}
getUseCaseCallback().onSuccess()
回调方法就是UseCaseHandler中的内部类UiCallbackWrapper中的方法
@Override
public void onSuccess(V response) {
mUseCaseHandler.notifyResponse(response, mCallback);
}
也就是执行了mUseCaseHandler.notifyResponse(response, mCallback)
方法
public void notifyResponse(final V response,
final UseCase.UseCaseCallback useCaseCallback) {
mUseCaseScheduler.notifyResponse(response, useCaseCallback);
}
也就是执行了mUseCaseScheduler.notifyResponse
方法
@Override
public void notifyResponse(final V response, final UseCase.UseCaseCallback useCaseCallback) {
mHandler.post(new Runnable() {
@Override
public void run() {
useCaseCallback.onSuccess(response);
}
});
}
到了这里,就是在主线程中执行了返回数据的方法,就是在Presenter里面new的这个UseCaseCallback返回了数据
mUseCaseHandler.execute(mGetPictures, new GetPictures.RequestValues(), new UseCase.UseCaseCallback() {
@Override
public void onSuccess(GetPictures.ResponseValue response) {
mPicturesActivity.showPic(response.getPictures());
}
@Override
public void onError() {
}
});
最后交给mPicturesActivity进行更新界面,整个流程完成
//添加图片
@Override
public void addPic(Picture picture) {
mPictureAdapter.insertData(0, picture);
}
个人感觉这个架构还是很有用的,做业务逻辑比较多的APP还是很适合的,反正就是低耦合,每个业务都是独立的,复用性很高;如果配合上dagger依赖注入框架就更能感受到低耦合了;缺点就是每个小的逻辑都可以创建一个UseCase会创建更多的类
项目地址:https://github.com/tyhjh/AndroidMvp