是時候上車Jetpack了,內含音樂播放器實例

1. 背景

以前公司項目用的一直是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/…編程

2. 應用技術

基礎框架選用MVVM,選用的Jetpack組件包括Lifecycle、ViewModel、LiveData、DataBinDing、Navigation、Room設計模式

項目基於Navigation由單ActivityFragment實現,使用這種模式給我最直觀的感覺就是,好比點擊搜索進入搜索界面的銜接動畫,在多Activity之間是不可能這麼連貫的。bash

整個項目所有使用Kotlin語言,普遍應用了協程編寫了大量的擴展函數網絡

關於每一個模塊的職責我是這樣定義的:app

Model

對應項目中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 {
        ...
        ...
    }
}
複製代碼

ViewModel

基於Jetpack中的ViewModel進行封裝(友情提示:Jetpack ViewModelMVVM ViewModel沒有半毛錢關係,切勿將兩個概念混淆)。在項目中VM層職責很簡單,經過內部經過LiveData作數據存儲,以及結合DataBinding作數據綁定。函數

View

儘可能只作UI渲染。與MVP中不一樣,View是經過DataBinding與數據進行綁定,ActivityFragment很是輕盈只專一於生命週期的管理,數據渲染基本所有由DataBinding+BindAdapter實現。

關於MVVM模版類的封裝可至package com.zs.base_library.base(包名)下查看。

網絡層

關於網絡層繼續使用OkHttp Retrofit,並對Retrofit多ApiService以及多域名進行了封裝。配合Repository中封裝的協程使用美得不能再美。

數據庫

項目中歷史記錄是在本地數據庫進行維護的,關於數據庫使用了Jetpack中的Room

主題切換

Android原生提供的夜間切換好像又API版本限制,因此就沒有用。我我的在本地維護了兩套主題,可動態切換。當前包含白天、夜間兩套主題

3. 關於註釋

去年在個人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 {
     ....
     ....
}
複製代碼
  • 關於播放器的設計我以爲仍是有些地方值得和你們分享,後面我會單獨寫一篇文章進行分析。

寫在最後

此項目中你很難看到不明不白的代碼。JetpackKotlin是大勢所趨,既然拒毫不了那何不開心的擁抱。功能目前已完成90%,代碼也在持續優化,歡迎你們關注、下載源代碼,讓咱們共同窗習、共同進步。

點個贊你就是世界上第二帥的人,給個star你就能超越我成爲第一帥~_~

再次附上github:github.com/zskingking/…,若是以爲對你有幫助麻煩給個star

相關文章
相關標籤/搜索