如何实现自己的Android MVP框架
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何实现自己的Android MVP框架相关的知识,希望对你有一定的参考价值。
参考技术A 前言MVP作为一种MVC的演化版本在android开发中受到了越来越多的关注,但在项目开发中选择一种这样的软件设计模式需保持慎重心态,一旦确定使用MVP作为你App的开发模式那么你就最好坚持做下去,如果在使用MVP模式开发过程中发现问题而且坑越来越大,这时你想用MVC等来重新设计的话基本上就等于推倒重来了。要知道在Android上MVP在现在为止并没有统一的标准或者框架,不像SSH这三个成熟稳重强而有力的三剑客支持推动着JavaEE的开发,所以在运用MVP时一定要做好自己的理解,并且尽量预知自己App各模块的需求(客户说改改改,我们就改改改:-()以便提前做好充分的设计工作。当然MVP既然能出现那么必然有它的优点的,不然谁会理会这个冒出来的东西,下面就对Android中MVP做一些阐述。MVP简介相信大家对MVC都是比较熟悉了:M-Model-模型、V-View-视图、C-Controller-控制器,MVP作为MVC的演化版本,也是作为用户界面(用户层)的实现模式,那么类似的MVP所对应的意义:M-Model-模型、V-View-视图、P-Presenter-表示器。从MVC和MVP两者结合来看,Controlller/Presenter在MVC/MVP中都起着逻辑控制处理的角色,起着控制各业务流程的作用。而MVP与MVC最不同的一点是M与V是不直接关联的也是就Model与View不存在直接关系,这两者之间间隔着的是Presenter层,其负责调控View与Model之间的间接交互,MVP的结构图如下所示,对于这个图理解即可而不必限于其中的条条框框,毕竟在不同的场景下多少会有些出入的。在Android中很重要的一点就是对UI的操作基本上需要异步进行也就是在MainThread中才能操作UI,所以对View与Model的切断分离是合理的。此外Presenter与View、Model的交互使用接口定义交互操作可以进一步达到松耦合也可以通过接口更加方便地进行单元测试。MVP结构图MVP之ModelModel是用户界面需要显示数据的抽象,也可以理解为从业务数据(结果)那里到用户界面的抽象(Businessrule,dataaccess,modelclasses)。本文Demo为了简单处理就直接把业务放到了对应的Model之中。MVP之View视图这一层体现的很轻薄,负责显示数据、提供友好界面跟用户交互就行。MVP下Activity和Fragment体现在了这一层,Activity一般也就做加载UI视图、设置监听再交由Presenter处理的一些工作,所以也就需要持有相应Presenter的引用。例如,Activity上滚动列表时隐藏或者显示Acionbar(Toolbar),这样的UI逻辑时也应该在这一层。另外在View上输入的数据做一些判断时,例如,EditText的输入数据,假如是简单的非空判断则可以作为View层的逻辑,而当需要对EditText的数据进行更复杂的比较时,如从数据库获取本地数据进行判断时明显需要经过Model层才能返回了,所以这些细节需要自己掂量。MVP之PresenterPresenter这一层处理着程序各种逻辑的分发,收到View层UI上的反馈命令、定时命令、系统命令等指令后分发处理逻辑交由业务层做具体的业务操作,然后将得到的Model给View显示。演示demo动手写起代码来才有更好的感觉。demo很简单,还是上个图更直观,输入城市的代号,点击按钮获取城市的天气信息然后显示出来,网络操作使用Volley框架,解析用Gson,其它的就手写了。整个项目的包设计如下:包结构项目效果预览包图中明显的三层:Model包、Presenter包、UI包,其中,三者都实现各自的结构,Model为WeatherModel、Presenter为WeatherPresenter、View为Weather,那么具体实现类就是impl包里的了,View层的即为Activity。此外的app和util包无关紧要可以不看。可以看到采用MVP设计后项目明显多了很多东西,这也是不可避免的,使用原始方法可以使项目开起来简单些但是以后还有维护呢、测试呢、加功能呢、。。。entity里的实体属性基本上对应json里的这些属性了,代码不贴了View里面的接口:publicinterfaceWeatherViewvoidshowLoading();voidhideLoading();voidshowError();voidsetWeatherInfo(Weatherweather);WeatherPresenter的接口:publicinterfaceWeatherPresenter/***获取天气的逻辑*/voidgetWeather(StringcityNO);WeatherModel接口:publicinterfaceWeatherModelvoidloadWeather(StringcityNO,OnWeatherListenerlistener);prestener里面还有个OnWeatherListener,其在Presenter层实现,给Model层回调,更改View层的状态,确保Model层不直接操作View层。如果没有这一接口在WeatherPresenterImpl实现的话,WeatherPresenterImpl只有View和Model的引用那么Model怎么把结果告诉View呢?当然这只是一种解决方案,在实际项目中可以使用Dagger、EventBus、Otto等第三方框架结合进来达到更加松耦合的设计。publicinterfaceOnWeatherListener/***成功时回调**@paramweather*/voidonSuccess(Weatherweather);/***失败时回调,简单处理,没做什么*/voidonError();所以demo的代码流程:Activity做了一些UI初始化的东西并需要实例化对应WeatherPresenter的引用和实现WeatherView的接口,监听界面动作,Go按钮按下后即接收到查询天气的事件,在onClick里接收到即通过WeatherPresenter的引用把它交给WeatherPresenter处理。WeatherPresenter接收到了查询天气的逻辑就知道要查询天气了,然后把查询天气的具体业务实现交给WeatherModel去实现同时把WeatherListener即WeatherPresenter自己传给WeatherModel。WeatherModel进行查询天气业务后即把结果通过WeatherListener回调通知WeatherPresenter,WeatherPresenter再把结果返回给View层的Activity,最后Activity显示结果。就这样,拍砖之处请拍。End采用哪种软件设计模式都是为了达到如下目的,找到合适的加以运用就是最好的:易于维护易于测试松耦合度复用性高健壮稳定------------------------------------------------------------------------------------以上内容均参考互联网,希望以上知识对您有所帮助,Android MVP-编程思想2(代码实现初级版)
前言
通过一个小的登录功能模块案例,帮助大家了解MVP。最终实现一个结合Rxjava2,Retrofit 的MVP通用框架。代码放到github上。(如有错误之处,请在评论区指出,谢谢。如果感觉写的不错,请点赞,关注,谢谢。)
目录:
Android MVP-编程思想1(什么是MVC-MVP-MVVM模式?)
Android MVP-编程思想2(代码实现初级版)
Android MVP-编程思想3(内存泄露问题处理,基类封装,有没有必要再使用软引用?)
Android MVP-编程思想4(AOP思想-动态代理运用,反射创建M层实例对象)
Android MVP-编程思想5(如何处理多个P层的问题?)
Android MVP-编程思想6(依赖注入多个P层优化—注解,反射)
Android MVP-编程思想7(为什么使用代理类抽取通用方法而不是工具类?,基类BaseMvpFragment)
未完待续--------
Android MVP-编程思想8(集成Rxjava2,Retrofit)
MVP 实战
那么我们下面就要将这个类中的代码改写为 MVP 的写法,回顾上面提及的 MVP 架构的思想,它是将 View 层与 Model 层彻底隔离,意味着 View 和 Model 都不再持对方的引用,它们通过一个第三者 Presenter 来代理事物的传递,所以 Presenter 层会持有 Model 与 View 层的引用,这是第一步。
第二步,是将它们之间的联系抽象出来,以接口的方式相互调用,所以 Model 、View、Presenter 各自拥有自己的接口和抽象方法,所以这就会无形的多出了三个接口类,这也就是 MVP 的缺点之一。所以,为了较少的创建接口类,我们就给这三层接口定义了一个契约接口,把它们更加紧密的结合在一起,方法查看,例如代码这样写:参考谷歌官方mvp案例todo-mvp
下面我们以登录功能为例行了解 MVP 架构的代码写法。
第一步
首先我们参考谷歌todo-mvp 写一个契约类,定义交互行为。契约类的目的是把MVP的行为定义放在一起,方便阅读和维护
public class LoginContract
/**
* V 定义View的行为,调用者是P
*/
interface IView
//登录成功跳转到主界面
void goToMainActivity();
//请求网络显示等待框
void showProgress(boolean isShow);
//显示提示框-登录失败,或其他提示信息
void showToast(String string);
/**
* P(中转站和数据处理者) 定义View与Model的交互行为,调用者是V。P做中转
*/
interface IPresenter
void onClickLogin(String username, String pwd);
/**
* M 数据提供者
*/
interface IModel
void getUserInfo(String username, String pwd, ICallBack callBack);
第二步,定义MVP行为接口的实现类
辅助类ICallBack,用来回调http请求的数据
public interface ICallBack<T>
void success(T data);
void error(String error);
M层的实现类LoginModel,提供数据,网络数据,本地数据
public class LoginModel implements LoginContract.IModel
@Override
public void getUserInfo(String username, String pwd, final ICallBack callBack)
//todo 模拟 http请求
Handler handler = new Handler();
Runnable runnable = new Runnable()
@Override
public void run()
callBack.success("成功");
;
handler.postDelayed(runnable, 2000);
V层的实现类LoginActivity,控制视图显示,要调用P层接口,所以要持有P对象的引用
public class LoginActivity extends AppCompatActivity implements LoginContract.IView
LoginContract.IPresenter mPresenter;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPresenter = new LoginPresenter(this);
findViewById(R.id.btn_login).setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View view)
mPresenter.onClickLogin("xxx", "xxx");
);
@Override
public void goToMainActivity()
runOnUiThread(new Runnable()
@Override
public void run()
//todo 登录成功跳转到主界面
Log.i("mvp_", "登录成功跳转到主界面");
);
@Override
public void showProgress(boolean isShow)
if (isShow)
Log.i("mvp_", "显示等待框");
else
Log.i("mvp_", "隐藏等待框");
@Override
public void showToast(String str)
Toast.makeText(this, str, Toast.LENGTH_LONG).show();
P层实现类LoginPresenter,是M和V交互的协调者,所以要持有M,V对象的引用
注意点:网络请求是耗时操作,我们 Presenter 层持有了 View 层的引用,也就是 Activity 的引用,在网络请求过程中,Activity被用户或者系统关闭,这时 View 相当于被摧毁了,如果不进行判断 View 引用是否为空,当数据返回时,就会造成空指针异常,所以每次都要进行判空才合理。
public class LoginPresenter implements LoginContract.IPresenter
private LoginContract.IModel mModel;
private LoginContract.IView mView;
public LoginPresenter(LoginContract.IView view)
this.mModel = new LoginModel();
this.mView = view;
@Override
public void onClickLogin(String username, String pwd)
mView.showProgress(true);
mModel.getUserInfo(username, pwd, new ICallBack<String>()
@Override
public void success(String data)
if (mView != null)
mView.showToast("登录成功");
mView.showProgress(false);
mView.goToMainActivity();
@Override
public void error(String error)
if (mView != null)
mView.showProgress(false);
mView.showToast("登录失败");
);
上面使用最简单的方式,演示了MVP的设计思想。目的是让大家理解,MVP模式是如何把业务逻辑,数据与V层进行分离的。再回想对比MVC是不是觉得MVP模式,职责分离的更清楚。
运行,点击登录按钮,打印日志如下:---------------------
2019-12-11 14:34:06.990 6376-6376/chongchong.wei.rx_retrofit_mvp I/mvp_: 显示等待框
2019-12-11 14:34:09.002 6376-6376/chongchong.wei.rx_retrofit_mvp I/mvp_: 隐藏等待框
2019-12-11 14:34:09.002 6376-6376/chongchong.wei.rx_retrofit_mvp I/mvp_: 登录成功跳转到主界面
当然上面的代码只是帮助大家理解MVP模式的思想,一些细节问题没有处理。下一节我们对代码进行优化,抽离,封装。
以上是关于如何实现自己的Android MVP框架的主要内容,如果未能解决你的问题,请参考以下文章