完整的Android MVP開發之旅

開發背景

最近是在作一個與健身相關的APP,裏面有訓練器模塊基本功能是按照特色動做的演示和描述來引導用戶完成訓練。在第一個版本時因爲沒接觸過些類項目與功能花了幾周的時間大概1500行代碼才完成這個功能,
當時雖然我已經儘可能讓代碼表現的清晰,可是能夠想像到當一個Activity中包含這麼多代碼是什麼感受。本身維護起來都難受。java

先談設計

有了前一次設計經驗這次開發使用MVP、模塊化、面向接口等概念,將整個訓練器分爲控制器、數據模型、音頻、視圖、可訓練對象五個模塊分別用如下接口表示:數據庫

  • ITrainerController
  • ITrainerModel
  • IAudioFlow
  • ITrainerView
  • ITrainable

去掉一些抽象類後接口圖以下:服務器

接口靜態圖

設計以上接口後引入MVP概念使用ITrainerController作爲Presenter,ITrainerModel作Model,ITrainerView作View下面介紹主要模塊。網絡

控制器

能夠用在MVC和MVP中這取決於用哪一種開發模式,在我開發的項目控制器用來控制訓練器的運行管理訓練器的生命週期如訓練、暫停、休息、完成等狀態協調ITrainerModel、ITrainerView、IAudioFlow等各個模塊。
在使用過程當中控制器並不止一個這也是抽象出一個接口的緣由,ITrainerController接口繼承IPresenter接口使其能作爲Presenter使用。架構

控制器靜態圖

數據模型

數據模型中包含大量的ITrainable對象,對內組織數據對外提供數據支持。對數據的組織方式主要分兩種:機器學習

  1. 從本地數據庫
  2. 從網絡獲取

在訓練器中多是正常的訓練或是一次訓練測試而訓練數據和測試數據又有一些差別但它們的數據都被當作ITrainable,測試數據是不須要保存的只須要從服務器拉取後按要求完成就行而訓練是會產生本地記錄的。
針對不一樣數據組織方式提供不一樣的數據模型這是有必要的。模塊化

音頻

音頻比較多樣化像訓練過程當中包含動做名、時間、單位詞、提醒等音頻這些音頻都是分開的不一樣的音頻文件。Android主要有兩種實現方式:學習

  1. SoundPool
  2. 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);
    }

}

總結

成功的設計與架構能減小大量的工做時間,利用接口可以讓開發人員更加註重功能上的實現同時隔離各個模塊之間的依賴。下次產品經理再改需求或再整出個訓練模式時咱也能從容應對。因爲本人水平有限若有錯誤煩請指正我會在第一時間作出更改。




《架構文摘》天天一篇架構領域重磅好文,涉及一線互聯網公司應用架構(高可用、高性 能、高穩定)、大數據、機器學習等各個熱門領域。

相關文章
相關標籤/搜索