編輯:關於Android編程
開發背景
最近是在做一個與健身相關的APP,裡面有訓練器模塊基本功能是按照特點動作的演示和描述來引導用戶完成訓練。在第一個版本時由於沒接觸過些類項目與功能花了幾周的時間大概1500行代碼才完成這個功能,
當時雖然我已經盡量讓代碼表現的清晰,但是可以想像到當一個Activity中包含這麼多代碼是什麼感覺。自己維護起來都難受。
先談設計
有了前一次設計經驗此次開發使用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); } }
總結
成功的設計與架構能減少大量的工作時間,利用接口可讓開發人員更加注重功能上的實現同時隔離各個模塊之間的依賴。下次產品經理再改需求或再整出個訓練模式時咱也能從容應對。
由於本人水平有限如有錯誤,請大家諒解。
Setting是android系統很重要的模塊,這個模塊並不是很復雜,這部分也一直在看,很多時候都是在看某個具體的選項,比如WLAN,藍牙這樣具體的源碼,但是對於主界面的
一.摘要彈窗通常用於提示用戶進行某種操作,比如:點擊分享按鈕,彈窗分享對話框;雙擊返回按鈕,彈窗退出對話框;下載文件,提示下載對話框等等,分享對話框/退出對話框/下載對話
概要 對於mvp模式,大家都知道是由mvc演變而來的,對於MVC大家都知道 M Model(用於存放實體模型與業務邏輯) V View(存放布局和資源文件) C Co
此方法適用於所有母控件無法獲取焦點的情況 開發中很常見的一個問題,項目中的listview不僅僅是簡單的文字,常常需要自己定義listview,自己的Adapter去繼承