最近是在作一个与健身相关的APP,里面有训练器模块基本功能是按照特色动做的演示和描述来引导用户完成训练。在第一个版本时因为没接触过些类项目与功能花了几周的时间大概1500行代码才完成这个功能,
当时虽然我已经尽可能让代码表现的清晰,可是能够想像到当一个Activity中包含这么多代码是什么感受。本身维护起来都难受。java
有了前一次设计经验这次开发使用MVP、模块化、面向接口等概念,将整个训练器分为控制器、数据模型、音频、视图、可训练对象五个模块分别用如下接口表示:数据库
去掉一些抽象类后接口图以下:服务器
设计以上接口后引入MVP概念使用ITrainerController
作为Presenter,ITrainerModel作Model,ITrainerView作View下面介绍主要模块。网络
能够用在MVC和MVP中这取决于用哪一种开发模式,在我开发的项目控制器用来控制训练器的运行管理训练器的生命周期如训练、暂停、休息、完成等状态协调ITrainerModel、ITrainerView、IAudioFlow等各个模块。
在使用过程当中控制器并不止一个这也是抽象出一个接口的缘由,ITrainerController接口继承IPresenter接口使其能作为Presenter使用。架构
数据模型中包含大量的ITrainable对象,对内组织数据对外提供数据支持。对数据的组织方式主要分两种:机器学习
在训练器中多是正常的训练或是一次训练测试而训练数据和测试数据又有一些差别但它们的数据都被当作ITrainable,测试数据是不须要保存的只须要从服务器拉取后按要求完成就行而训练是会产生本地记录的。
针对不一样数据组织方式提供不一样的数据模型这是有必要的。模块化
音频比较多样化像训练过程当中包含动做名、时间、单位词、提醒等音频这些音频都是分开的不一样的音频文件。Android主要有两种实现方式:学习
首先说SoundPool优势天然就是免去了加载、管理音频等过程可是它并不适应咱们的训练器,主要缘由是缺乏准备、完成后的一些回调而在训练器运行过程当中这些过程必不可少好比在播放完一段预备开始后音频这时咱们才能进行正式的训练。
最后是采用MediaPlayer,可是在使用过程又要考虑到音频的集中管理与资源的释放免不了多封装一次。设计时我将所有音频逻辑放在Android Service中Activity经过bind AudioService来使用音频,将音频逻辑放入AudioService这样能够音频完
全独立起来使其能在后台播放而且也能够提升进程优先级。测试
在设计中AudioService仅仅播放与管理音频和资源并不具有音频播放的逻辑功能。因为不一样的训练方式音频的播放逻辑也有不一样之处因此在此设计IAudioFlow接口来负责音频逻辑。大数据
Android经常使用Activity做为视图,经过实现ITrainerView接口来完成训练视图的显示。视图中不包含任何业务逻辑代码。
说到实现其实这并非最须要关注的内容,由于上面提供了很全面的接口而咱们的模块又是使用的接口因此无论如何实现那些功能并不会对各个模块之间产生大的影响除非功能实现与实际要求相差太多。这里我只详细说一下音频模块的实现。
音频模块又可分为音频管理与音频业务逻辑。音频管理就是加载、播放、回收资源等功能,音频业务逻辑主要处理在正确的状态下应该播放什么样的音频。将整个音频管理模块放在Android Service中与业务逻辑彻底分离。音频模块涉及如下类与接口:
基本使用流程是首先经过绑定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); } }
成功的设计与架构能减小大量的工做时间,利用接口可以让开发人员更加注重功能上的实现同时隔离各个模块之间的依赖。下次产品经理再改需求或再整出个训练模式时咱也能从容应对。因为本人水平有限若有错误烦请指正我会在第一时间作出更改。
《架构文摘》天天一篇架构领域重磅好文,涉及一线互联网公司应用架构(高可用、高性 能、高稳定)、大数据、机器学习等各个热门领域。