以前公司項目用的一直是MVP
框架,我我的也在幾個月前基於鴻神 WanAndroid API
開發了一款MVP
版的App
,使用MVP
的過程最深的感覺是開發效率極低,每每寫一大堆接口,可複用的屈指可數。年初瞭解了Jetpack
模式下的MVVM
,在LiveData、ViewModel、DataBinDing
的加持下實現了單向依賴
和數據綁定
,代碼量大幅度減小,根據Jetpack
的特性項目穩定性也提高了很多。git
爲了更深刻的理解Jetpack
中各個組件,在前段時間基於Jetpack MVVM
又實現了一版WanAndroid
。相比上一版的MVP
增長了夜間模式
和音樂播放器
,播放器界面仿照網易雲音樂
。App
中也大量的使用屬性動畫讓界面簡約而不簡陋。先上圖look一波github
關於播放器當前只支持讀取本地音樂,若是想體驗能夠事先下載幾首歌
數據庫
先附上github:
github.com/zskingking/…編程
基礎框架選用MVVM
,選用的Jetpack
組件包括Lifecycle、ViewModel、LiveData、DataBinDing、Navigation、Room
。設計模式
項目基於Navigation
由單Activity
多Fragment
實現,使用這種模式給我最直觀的感覺就是快
,好比點擊搜索進入搜索界面的銜接動畫,在多Activity
之間是不可能這麼連貫的。bash
整個項目所有使用Kotlin
語言,普遍應用了協程
編寫了大量的擴展函數
。網絡
關於每一個模塊的職責我是這樣定義的:app
對應項目中Repository
,作數據請求以及業務邏輯。不少人將業務邏輯編寫到VM
層,但我我的認爲寫在Model
層更爲合適,由於數據和業務邏輯自己就是息息相關,拿到數據及時處理業務邏輯,最後經過ViewModel
注入的LiveData
將數據發送給View
層。在該層我也對協程作了封裝,以及統一捕獲處理錯誤信息。 代碼大概張這樣:框架
/**
* 錯誤方法
*/
typealias Error = suspend (e: ApiException) -> Unit
/**
* des 基礎數據層
* @date 2020/5/18
* @author zs
*
* @param coroutineScope 注入viewModel的coroutineScope用於協程管理
* @param errorLiveData 業務出錯或者爆發異常,由errorLiveData通知視圖層去處理
*/
open class BaseRepository(
private val coroutineScope: CoroutineScope,
private val errorLiveData: MutableLiveData<ApiException>
) {
/**
* 對協程進行封裝,統一處理錯誤信息
*
* @param block 執行中
* @param success 執行成功
*/
protected fun <T> launch(
block: suspend () -> T
, success: suspend (T) -> Unit
, error:Error? = null): Job {
return coroutineScope.launch {
runCatching {
withContext(Dispatchers.IO) {
block()
}
}.onSuccess {
success(it)
}.onFailure {
it.printStackTrace()
getApiException(it).apply {
error?.invoke(this)
toast(errorMessage)
//統一響應錯誤信息
errorLiveData.value = this
}
}
}
}
/**
* 捕獲異常信息
*/
private fun getApiException(e: Throwable): ApiException {
...
...
}
}
複製代碼
基於Jetpack
中的ViewModel
進行封裝(友情提示:Jetpack ViewModel
和MVVM ViewModel
沒有半毛錢關係,切勿將兩個概念混淆)。在項目中VM
層職責很簡單,經過內部經過LiveData
作數據存儲,以及結合DataBinding
作數據綁定。函數
儘可能只作UI渲染。與MVP
中不一樣,View是經過DataBinding與數據進行綁定,Activity
或Fragment
很是輕盈只專一於生命週期的管理,數據渲染基本所有由DataBinding+BindAdapter
實現。
關於MVVM
模版類的封裝可至package com.zs.base_library.base(包名)
下查看。
關於網絡層繼續使用OkHttp Retrofit
,並對Retrofit多ApiService
以及多域名進行了封裝。配合Repository
中封裝的協程使用美得不能再美。
項目中歷史記錄
是在本地數據庫進行維護的,關於數據庫使用了Jetpack
中的Room
。
Android
原生提供的夜間切換好像又API
版本限制,因此就沒有用。我我的在本地維護了兩套主題,可動態切換。當前包含白天、夜間
兩套主題
去年在個人Leader
強行督促下養成了寫註釋的規習慣,我我的對寫註釋的要求也愈來愈高。
項目中運用了大量的設計模式,每用到一種設計模式
我都會結合當時場景進行解釋,好比播放器中某個接口,我會這樣寫註釋:
/**
* des 全部的具體Player必須實現該接口,目的是爲了讓PlayManager不依賴任何
* 具體的音頻播放實現,緣由大概有兩點
*
* 1.PlayManager包含業務信息,Player不該該與業務信息進行耦合,不然每次改動都會對業務形成影響
*
* 2.符合開閉原則,若是須要對Player進行替換勢必會牽連到PlayManager中的業務,於是形成沒必要要的麻煩
* 若是基於IPlayer接口編程,擴展出一個Player便可,正所謂對擴展開放、修改關閉
*
* @author zs
* @data 2020-06-23
*/
interface IPlayer {
....
....
}
/**
* des 音頻管理
* 經過單例模式實現,託管音頻狀態與信息,而且做爲惟一的可信源
* 經過觀察者模式統一對狀態進行分發
* 實則是一個代理,將目標對象Player與調用者隔離,而且在內部實現了對觀察者的註冊與通知
* @author zs
* @data 2020/6/25
*/
class PlayerManager private constructor() : IPlayerStatus {
....
....
}
複製代碼
關於播放器的設計我以爲仍是有些地方值得和你們分享,後面我會單獨寫一篇文章進行分析。
此項目中你很難看到不明不白的代碼。Jetpack
和Kotlin
是大勢所趨,既然拒毫不了那何不開心的擁抱。功能目前已完成90%
,代碼也在持續優化,歡迎你們關注、下載源代碼,讓咱們共同窗習、共同進步。
點個贊你就是世界上第二帥的人,給個star你就能超越我成爲第一帥~_~
再次附上github:
github.com/zskingking/…,若是以爲對你有幫助麻煩給個star