是时候上车Jetpack了,内含音乐播放器实例
1. 背景
之前公司项目用的一直是MVP
框架,我个人也在几个月前基于鸿神 WanAndroid API
开发了一款MVP
版的App
,使用MVP
的过程最深的感受是开发效率极低,往往写一大堆接口,可复用的屈指可数。年初了解了Jetpack
模式下的MVVM
,在LiveData、ViewModel、DataBinDing
的加持下实现了单向依赖
和数据绑定
,代码量大幅度减少,根据Jetpack
的特性项目稳定性也提升了不少。
为了更深入的理解Jetpack
中各个组件,在前段时间基于Jetpack MVVM
又实现了一版WanAndroid
。相比上一版的MVP
增加了夜间模式
和音乐播放器
,播放器界面仿照网易云音乐
。App
中也大量的使用属性动画让界面简约而不简陋。先上图look一波
文章图片
image 关于播放器当前只支持读取本地音乐,如果想体验可以事先下载几首歌
先附上github:
https://github.com/zskingking/Jetpack-WanAndroid
2. 应用技术
基础框架选用MVVM
,选用的Jetpack
组件包括Lifecycle、ViewModel、LiveData、DataBinDing、Navigation、Room
。
项目基于Navigation
由单Activity
多Fragment
实现,使用这种模式给我最直观的感受就是快
,比如点击搜索进入搜索界面的衔接动画,在多Activity
之间是不可能这么连贯的。
整个项目全部使用Kotlin
语言,广泛应用了协程
编写了大量的扩展函数
。
关于每个模块的职责我是这样定义的:
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
) {/**
* 对协程进行封装,统一处理错误信息
*
* @param block执行中
* @param success 执行成功
*/
protected fun
ViewModel
基于Jetpack
中的ViewModel
进行封装(友情提示:Jetpack ViewModel
和MVVM ViewModel
没有半毛钱关系,切勿将两个概念混淆)。在项目中VM
层职责很简单,通过内部通过LiveData
做数据存储,以及结合DataBinding
做数据绑定。
View
尽量只做UI渲染。与MVP
中不同,View是通过DataBinding与数据进行绑定,Activity
或Fragment
非常轻盈只专注于生命周期的管理,数据渲染基本全部由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 {
....
....
}
写在最后关于播放器的设计我觉得还是有些地方值得和大家分享,后面我会单独写一篇文章进行分析。
此项目中你很难看到不明不白的代码。Jetpack
和Kotlin
是大势所趋,既然拒绝不了那何不开心的拥抱。功能目前已完成75%
,代码也在持续优化,欢迎大家关注、下载源代码,让我们共同学习、共同进步。
【是时候上车Jetpack了,内含音乐播放器实例】再次附上github:
https://github.com/zskingking/Jetpack-WanAndroid,如果觉得对你有帮助麻烦给个star