完整的Android MVP开发之旅
开发背景
最近是在做一个与健身相关的APP,里面有训练器模块基本功能是按照特点动作的演示和描述来引导用户完成训练。在第一个版本时由于没接触过些类项目与功能花了几周的时间大概1500行代码才完成这个功能,
当时虽然我已经尽量让代码表现的清晰,但是可以想像到当一个Activity中包含这么多代码是什么感觉。自己维护起来都难受。
先谈设计
有了前一次设计经验此次开发使用MVP、模块化、面向接口等概念,将整个训练器分为控制器、数据模型、音频、视图、可训练对象五个模块分别用以下接口表示:
- ITrainerController
- ITrainerModel
- IAudioFlow
- ITrainerView
- ITrainable
去掉一些抽象类后接口图如下:

设计以上接口后引入MVP概念使用ITrainerController做为Presenter,ITrainerModel做Model,ITrainerView做View下面介绍主要模块。
控制器
可以用在MVC和MVP中这取决于用哪种开发模式,在我开发的项目控制器用来控制训练器的运行管理训练器的生命周期如训练、暂停、休息、完成等状态协调ITrainerModel、ITrainerView、IAudioFlow等各个模块。
在使用过程中控制器并不止一个这也是抽象出一个接口的原因,ITrainerController接口继承IPresenter接口使其能做为Presenter使用。

数据模型
数据模型中包含大量的ITrainable对象,对内组织数据对外提供数据支持。对数据的组织方式主要分两种:
- 从本地数据库
- 从网络获取
在训练器中可能是正常的训练或是一次训练测试而训练数据和测试数据又有一些差异但它们的数据都被当做ITrainable,测试数据是不需要保存的只需要从服务器拉取后按要求完成就行而训练是会产生本地记录的。
针对不同数据组织方式提供不同的数据模型这是有必要的。
音频
音频比较多样化像训练过程中包含动作名、时间、单位词、提醒等音频这些音频都是分开的不同的音频文件。Android主要有两种实现方式:
- SoundPool
- MediaPlayer
首先说SoundPool优点自然就是免去了加载、管理音频等过程但是它并不适应我们的训练器,主要原因是缺少准备、完成后的一些回调而在训练器运行过程中这些过程必不可少比如在播放完一段预备开始后音频这时我们才能进行正式的训练。
最后是采用MediaPlayer,但是在使用过程又要考虑到音频的集中管理与资源的释放免不了多封装一次。设计时我将全部音频逻辑放在Android Service中Activity通过bind AudioService来使用音频,将音频逻辑放入AudioService这样可以音频完
全独立起来使其能在后台播放并且也可以提高进程优先级。

在设计中AudioService仅仅播放与管理音频和资源并不具备音频播放的逻辑功能。由于不同的训练方式音频的播放逻辑也有不同之处所以在此设计IAudioFlow接口来负责音频逻辑。
训练视图
Android常用Activity作为视图,通过实现ITrainerView接口来完成训练视图的显示。视图中不包含任何业务逻辑代码。
再谈实现
说到实现其实这并不是最需要关注的内容,因为上面提供了很全面的接口而我们的模块又是使用的接口所以不管如何实现那些功能并不会对各个模块之间产生大的影响除非功能实现与实际要求相差太多。这里我只详细说一下音频模块的实现。
音频实现
音频模块又可分为音频管理与音频业务逻辑。音频管理就是加载、播放、回收资源等功能,音频业务逻辑主要处理在正确的状态下应该播放什么样的音频。将整个音频管理模块放在Android Service中与业务逻辑完全分离。音频模块涉及以下类与接口:
- AudioService: 音频服务器继承Android Service
- IAudioService: 音频抽象接口包含播放、暂停等事件
- MediaPlayerHolder: 持有MediaPlayer管理MediaPlayer生命同期
- IAudioFlow: 为不同的训练内容提供音频逻辑
- AudioServiceImpl: 实现IAudioService
基本使用流程是首先通过绑定AudioService的onBind方法返回IAudioService的实现类供IAudioFlow使用,IAudioFlow持有IAudioService实现后加载训练音频然后供ITrainerController使用。在AudioServiceImpl中会维持一个音频优化级队列,
上面提到因为音频都是不在一个文件中的所有需要在使用时将它们连接起来形成一段音频。通过优先级队列结合MediaPlayer播放完成时回调可以将多个音频组合在一起形成需要的音频。由于音频的播放越来越多MediaPlayer的回收利用特别重
要在AudioServiceImpl同样也具备MediaPlayer的回收与利用功能。这个功能实现是通过MediaPlayerHolder来处理的,通过MediaPlayerHolder的静态get方法获取MediaPlayerHolder如果回收池中有空闲的MediaPlayerHolder则拿来用没有时则
新建一个,同样也在一个音频播放完成后调用MediaPlayerHolder的recycle来进行回收利用。
模块整合
为减少依赖模块之间的整合需提供管理或帮助类,新建TrainerHelper来创建模块实现类其中包含一个Mode枚举来列举训练模式。
public class TrainerHelper {
public enum Mode{TEST, TRAINING, EXAM}
private static Mode mode;
public static void setMode(Mode m){
mode = m;
}
public static ITrainerController createPresenter(ITrainerView view, Bundle createArgs){
return new TrainerPresenter(view,createArgs);
}
public static ITrainerModel createTrainerModel(ITrainerController controller){
return = new DefaultTrainerModel(bundle);;
}
public static IAudioFlow createTrainerAudioFlow(ITrainerController controller){
return new DefaultAudioFlow(controller);
}
}
总结
成功的设计与架构能减少大量的工作时间,利用接口可让开发人员更加注重功能上的实现同时隔离各个模块之间的依赖。下次产品经理再改需求或再整出个训练模式时咱也能从容应对。由于本人水平有限如有错误烦请指正我会在第一时间做出更改。
《架构文摘》每天一篇架构领域重磅好文,涉及一线互联网公司应用架构(高可用、高性 能、高稳定)、大数据、机器学习等各个热门领域。
完整的Android MVP开发之旅的更多相关文章
- Android开发之旅: Intents和Intent Filters(理论部分)
引言 大部分移动设备平台上的应用程序都运行在他们自己的沙盒中.他们彼此之间互相隔离,并且严格限制应用程序与硬件和原始组件之间的交互. 我们知道交流是多么的重要,作为一个孤岛没有交流的东西,一定毫无意义 ...
- Android 开发之旅:深入分析布局文件&又是“Hello World!”
http://www.cnblogs.com/skynet/archive/2010/05/20/1740277.html 引言 上篇可以说是一个分水岭,它标志着我们从Android应用程序理论进入实 ...
- Android开发之旅5:应用程序基础及组件
引言 上篇Android开发之旅:应用程序基础及组件介绍了应用程序的基础知识及Android的四个组件,本篇将介绍如何激活组关闭组件等.本文的主题如下: 1.激活组件:意图(Intents) 1.1. ...
- Android开发之旅4:应用程序基础及组件
引言 为了后面的例子做准备,本篇及接下来几篇将介绍Android应用程序的原理及术语,这些也是作为一个Android的开发人员必须要了解,且深刻理解的东西.本篇的主题如下: 1.应用程序基础 2.应用 ...
- Android开发之旅3:android架构
引言 通过前面两篇: Android 开发之旅:环境搭建及HelloWorld Android 开发之旅:HelloWorld项目的目录结构 我们对android有了个大致的了解,知道如何搭建andr ...
- Android开发之旅2:HelloWorld项目的目录结构
引言 前面Android开发之旅:环境搭建及HelloWorld,我们介绍了如何搭建Android开发环境及简单地建立一个HelloWorld项目,本篇将通过HelloWorld项目来介绍Androi ...
- Cocos2d-x 3.x游戏开发之旅
Cocos2d-x 3.x游戏开发之旅 钟迪龙 著 ISBN 978-7-121-24276-2 2014年10月出版 定价:79.00元 516页 16开 内容提要 <Cocos2d-x ...
- ArcGIS Engine开发之旅05---空间数据库
原文:ArcGIS Engine开发之旅05---空间数据库 1 Geodatabase概念 Geodatabase是ArcInfo8引入的一种全新的面向对象的空间数据模型,是建立在DBMS之上的统 ...
- Android混合开发之WebViewJavascriptBridge实现JS与java安全交互
前言: 为了加快开发效率,目前公司一些功能使用H5开发,这里难免会用到Js与Java函数互相调用的问题,这个Android是提供了原生支持的,不过存在安全隐患,今天我们来学习一种安全方式来满足Js与j ...
随机推荐
- Javaweb MVC设计模式
Javaweb MVC设计模式 一.Java EE开发流程 二.MVC设计模式 什么是MVC? MVC是Model-View-Controller的简称,即模型-视图-控制器. MVC是一种设计模式, ...
- 理解Java反射机制
理解Java反射机制 转载请注明出处,谢谢! 一.Java反射简介 什么是反射? Java的反射机制是Java特性之一,反射机制是构建框架技术的基础所在.灵活掌握Java反射机制,对学习框架技术有很大 ...
- Vue 应用 nginx 配置 前后端不分离模式
一.先在官网下载nginx 软件,解压后放在软件盘中如D盘 将nginx 文件夹拖到编译器中,打开conf 文件夹中的 nginx.conf 文件,找到其中的server {} 配置项,默认35 行. ...
- SqlServer关于“无法删除数据库 "XXXX",因为该数据库当前正在使用”问题的解决方案
引言 在项目中,通过使用SQL语句“DROP DATABASE [数据库名]”删除数据时,一直出现“无法删除数据库 "XXXX",因为该数据库当前正在使用”的错误信息,经测试在Sq ...
- Jmeter 从数据库查询多个字段,依次传给登录接口怎么实现?
问题背景: 博文“Jmeter 如何把数据库的数据依次获取作为参数传入下一个请求?附栗子”某天有人留言如下: 看了下当时写的文章,如果从数据库查询多个字段,依次传给登录接口,确实不能合理实现,所以,特 ...
- JMeter简介及使用JMeter来访问网站
参考: http://jmeter.apache.org/ http://blog.chinaunix.net/uid-26884465-id-3416869.html http://www.ltes ...
- CodeForces 464 B Restore Cube
Restore Cube 题解: x->yyy 其实就是把x代替成yyy这个值. 如果不好理解的话, 可以试想一下, 刚开始的话 0->0, 1->1, 2->2,...,9- ...
- Atcode B - Colorful Hats(思维)
题目链接:http://agc016.contest.atcoder.jp/tasks/agc016_b 题解:挺有意思的题目主要还是模拟出最多有几种不可能的情况,要知道ai的差距不能超过1这个想想就 ...
- Gym 101470 题解
A:Banks 代码: #include<bits/stdc++.h> using namespace std; #define Fopen freopen("_in.txt&q ...
- VS Code 前端开发常用快捷键插件
一.vs code 的常用快捷键 1.注释:a) 单行注释:[ctrl+k,ctrl+c] 或 ctrl+/ b) 取消单行注释:[ctrl+k,ctrl+u] (按下ctrl不放,再按k + u) ...