Android MVP实践
在上篇博文 一款安卓TV上使用的蓝牙和WiFi测试工具 的基础上再记录一下学校MVP架构的心得,方便理解和后续追溯。
如有不对的地方,请指正交流
在18年年初和别人配合协同开发开机向导的时候,carry搭建了整个项目的大框架,而我也是相当于第一次参与了一个完整的项目开发。
协同开发期间,发现代码里面充满了框架的味道,各种Present的使用,而我看到了一脸懵逼。在只大概知道Activity怎么使用的人面前,突然像摆出了一本武林秘籍,而此秘籍生涩难懂,但却为我打开了另外一个世界。
经过搜索后,发现这是一种叫做MVP的框架的东西,属于在我们了解的MVC的基础上更好一些的框架。也是几年前的东西了,但作为一个接触android多年的老鸟,开始接触上层的菜鸟来说,需要实践实践把这些东西重新转换为自己的知识。
最近刚好有这么一个练手的机会,当然不能放过,练手的机会是需要开发一个蓝牙和wifi的测试工具。
需求很简单:
AndroidTV上需要跑一个apk,此apk需要可以实时监测接入的wifi和蓝牙测试模块板是否正常工作。
正常工作的wifi模块可以搜索到软件设置的wifi ap,信号强度和mac需要不越界等。
蓝牙模块类似,需要可以完整的检测到软件设置的蓝牙设备,mac不越界。
软件可以支持Wifi和蓝牙模块的热插拔重复测试。
软件流程图如下:
MVC模型的缺点:
MVC模型Activity是意义上的View层,但其中操作了control层业务逻辑,并没有做到完全分离,Activity代码太多臃肿,增加了维护、协同开发的难度。
MVP模型的简化图:
MVP模型中将View层和Model数据层完全去耦合,不在发生关联。Presenter作为中间商,承担了更为繁重的任务,所有业务逻辑相关的功能全部都交由Presenter来主导,而View层和Model数据层更多的是被动的接口调用关系。
和View相关的接口全部抽象到IView中,和业务逻辑相关的接口全部抽象到IPresenter中,精简了Activity的繁杂,拿到代码看的话虽然会有些绕,但利于后续功能扩展和维护。
代码整体目录结构
将View相关的目录:ui和view
ui中主要存放了Activity相关的代码。adapter子目录中是Activity中绘制界面时需要加载的listview时自定义的适配器;baseActivity是公共Activity,里面处理了一些公共的功能,如锁屏保功能等。
View中主要存放了自定义的view组件,base子目录存放了一些公共的view。
和数据相关的目录:model
config目录中主要存放了数据实体类,base子目录下是公共的数据实体,BtConfigItemData和WifiCOnfigItemData是蓝牙和wifi相关的数据结构体。
help目录提供了蓝牙和wifi和数据相关的功能,包括数据的获取和保存等。
注意:
虽然View层和Model层在MVP模型中是松耦合的,但是Model层的数据结构体在定义的时候其中包含的属性需要和View可以对应起来。因为自定义的View在刷新的时候需要用到和它相匹配的数据才行,所以Model层的数据结构的定义来源是View的样式和View刷新时需要的数据形式。
控制相关的目录:persenter
presenter中主要存放了具体的业务逻辑相关的操作文件,ISettingPresent是和设置页面触发过来的业务逻辑接口,IDetectPresenter是在检测过程的逻辑接口,有的是从UI中触发而来的,有的是从广播中回调过来的逻辑;impl子目录中主要是这些业务逻辑接口的具体实现。
utils目录:主要存放了一些工具类。
ScreenUtil是在不同分辨率屏幕上的适配的工具类;
SPUtils是Sharepreference轻量级数据存储相关的工具类;
Resource是String字符串资源的动态获取相关的工具类;
L是日志相关的工具类;
Calculate是和数据计算相关的工具类。
common目录:是项目的公共杂项相关
AndroidBroadcast类中主要定义和广播相关的操作;
ConfigConst中定义项目使用的一些常量,包括一些数据常量和自定义的枚举常量等。
api目录:
主要存放了和蓝牙和wifi相关功能接口,对framework中蓝牙和wifi相关接口的功能进行封装供我门项目使用。
MVP中两个关键点:
1、Presenter是持有View和Model的引用的,才可以去更新View和Model层的东西;
2、View中是持有Presenter的引用,所有才能把以前在MVC中交给Activity中控制的动作,转移到Presenter层去实现。
举例说明
1、创建View相关的接口,如刷新View。
public interface IDetectView {
void refreshWifiView(List<?> list, String mac, WifiCheckResult checkResult);
void refreshBtView(List<?> list, String mac, BtCheckResult checkResult);
}
2、创建和业务逻辑相关的接口
public interface IDetectPresenter {
void startDetect();
void stopDetect();
}
3、Activity中需要去实现View相关的接口,做UI刷新的真正操作;
Activity中想利用presenter来处理逻辑,需要先创建一个presenter的引用,Activity中可以通过持有的presenter接口去调用到逻辑层的接口;
Presenter接口的实现类在构造的时候需要将View的接口对象作为参数传过去,一般是实现view接口的Activity本身;
presenter的实现中根据传过来的view接口参数可以进行view相关接口的调用。
public class DetectActivity extends BaseActivity implements IDetectView{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
detectLayout = new DetectLayout(this);
setContentView(detectLayout);
//关键操作:将presenter和view关联起来
detectPresenter = new DetectPresenterImpl(this);
detectPresenter.startDetect();
}
@Override
public void refreshWifiView(List<?> list, String mac, WifiCheckResult checkResult) {
L.d("refreshWifiView");
}
@Override
public void refreshBtView(List<?> list, String mac, ConfigConst.BtCheckResult checkResult) {
L.d("refreshBtView");
}
}
4、在DetectPresenterImpl中做业务逻辑接口的实现;
public class DetectPresenterImpl implements IDetectPresenter {
private IDetectView detectView;
public DetectPresenterImpl(IDetectView view) {
//一般是实现IDetectView 接口的Activity对象
detectView = view;
//可以拿到model的引用来操作数据
......
}
@Override
public void startDetect() {
}
@Override
public void stopDetect() {
}
private void refreshWifiView() {
detectView.refreshWifiView(wifiResultData, wifiAddr, wifiCheckResult);
}
private void refreshBtView() {
detectView.refreshBtView(btResultList, btMac, btCheckResult);
}
}
微信公众号:一点微时光
欢迎关注我,一起学习,一起进步!
GitHub源码项目地址:
上一篇: 做站的方向比努力更重要
下一篇: 修改文件MD5