使用Jetpack全家桶+Kotlin实现的Android社区App加音乐播放器

Overview

注意注意

as升级到4.2后jdk location默认是11,而本项目jdk 1.8所以有冲突,会报java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException错误。具体解决方案 https://stackoverflow.com/questions/67410927/java-lang-noclassdeffounderror-javax-xml-bind-jaxbexception-after-updating-to-a

建议结合本人两篇关于架构的文章去理解项目

引入Jetpack架构后,你的App会发生哪些变化?

关于Android架构,你是否还在生搬硬套?

1. 背景

为了更深入的理解Jetpack中各个组件,在前段时间基于Jetpack MVVM实现了一版 WanAndroid。相比上一版的MVP增加了夜间模式音乐播放器,播放器界面仿照网易云音乐App中也大量的使用属性动画让界面简约而不简陋。先上图look一波

2. 应用技术

基础框架选用MVVM,选用的Jetpack组件包括Lifecycle、ViewModel、LiveData、DataBinDing、Navigation、Room

项目基于Navigation由单ActivityFragment实现,使用这种模式给我最直观的感受就是,比如点击搜索进入搜索界面的衔接动画,在多Activity之间是不可能这么连贯的。

整个项目全部使用Kotlin语言,广泛应用了协程编写了大量的扩展函数

关于每个模块的职责我是这样定义的:

Model

对应项目中Repository,做数据请求以及业务逻辑。

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以及多域名进行了封装。

数据库

项目中历史记录是在本地数据库进行维护的,关于数据库使用了Jetpack中的Room

主题切换

Android原生提供的夜间切换好像又API版本限制,所以就没有用。我个人在本地维护了两套主题,可动态切换。当前包含白天、夜间两套主题

3. 关于注释

我个人是非常喜欢写注释的,基本每一个复杂的功能都有对应的文字描述

项目中运用了大量的设计模式,每用到一种设计模式我都会结合当时场景进行解释,比如播放器中某个接口,我会这样写注释:


/**
 * des 所有的具体Player必须实现该接口,目的是为了让PlayManager不依赖任何
 *     具体的音频播放实现,原因大概有两点
 *
 *     1.PlayManager包含业务信息,Player不应该与业务信息进行耦合,否则每次改动都会对业务造成影响
 *
 *     2.符合开闭原则,如果需要对Player进行替换势必会牵连到PlayManager中的业务,因而造成不必要的麻烦
 *       如果基于IPlayer接口编程,扩展出一个Player即可,正所谓对扩展开放、修改关闭
 *
 * @author zs
 * @date 2020-06-23
 */
interface IPlayer {
    ....
    ....
}

/**
 * des 音频管理
 *     通过单例模式实现,托管音频状态与信息,并且作为唯一的可信源
 *     通过观察者模式统一对状态进行分发
 *     实则是一个代理,将目标对象Player与调用者隔离,并且在内部实现了对观察者的注册与通知
 * @author zs
 * @date 2020/6/25
 */
class PlayerManager private constructor() : IPlayerStatus {
     ....
     ....
}

写在最后

此项目中你很难看到不明不白的代码。JetpackKotlin是大势所趋,既然拒绝不了那何不开心的拥抱。功能目前已完成90%,代码也在持续优化,欢迎大家关注、下载源代码,让我们共同学习、共同进步。

Comments
  • 自定义颜色引用报错

    自定义颜色引用报错

    我想把项目重新写一下 然后splash 中 android:background="?attr/main_bg_1" 的颜色引用 报错 Failed to resolve attribute at index 13: TypedValue{t=0x2/d=0x7f0301da a=-1} 主题和自定义颜色也复制到新项目了 请问大神是什么原因

    opened by wkboys 7
  • build错误

    build错误

    一直卡在ERROR: Unknown host 'd29vzk4ow07wi7.cloudfront.net'. You may need to adjust the proxy settings in Gradle
    Could not get resource 'https://plugins.gradle.org/m2/org/jetbrains/kotlin/kotlin-annotation-processing-gradle/1.3.21/kotlin-annotation-processing-gradle-1.3.21.jar'

    opened by JumpingError 4
  • 关于框架的一些疑虑

    关于框架的一些疑虑

    谢谢大佬的框架,我在挑选框架的时候看到了大佬的库,感觉很不错。不过其中有一些小疑虑。

    1、就是databinding与业务的紧密耦合,导致页面的复用性很差,大佬可有想过用其他方式去实现。 2、我看一个UI组件(fragment,activity)中只能使用一个viewModel与页面的databinding 通过DataBindingConfig绑定,如果一个fragment或者actvity中有多个业务的组合,即可能有多个viewmodel同时作为一个UI单元的数据源时,框架该如何做?

    最后再次谢谢大佬的框架。

    opened by x930073498 2
  • 通过buildSrc统一管理依赖库

    通过buildSrc统一管理依赖库

    请教一下,你于2020年9月17号添加的“buildSrc”是个什么模块?怎么与“app”模块和“base-library”模块产生联系的,怎么在app和base-library中就能直接调用 buildSrc中的那几个objec类了?我看了半天,没有搞懂,烦请兄弟有空帮忙释疑一下,感谢。 ps:代码写的的确很美观

    opened by fakeSleeper 2
  • 项目请求数据报错

    项目请求数据报错

    2021-05-14 15:06:59.177 15290-15290/com.zs.zs_jetpack W/System.err: java.net.SocketTimeoutException: failed to connect to www.wanandroid.com/47.104.74.169 (port 443) from /192.168.17.44 (port 46810) after 10000ms 2021-05-14 15:06:59.177 15290-15290/com.zs.zs_jetpack W/System.err: at libcore.io.IoBridge.connectErrno(IoBridge.java:185) 2021-05-14 15:06:59.178 15290-15290/com.zs.zs_jetpack W/System.err: at libcore.io.IoBridge.connect(IoBridge.java:129) 2021-05-14 15:06:59.178 15290-15290/com.zs.zs_jetpack W/System.err: at java.net.PlainSocketImpl.socketConnect(PlainSocketImpl.java:137) 2021-05-14 15:06:59.178 15290-15290/com.zs.zs_jetpack W/System.err: at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:391) 2021-05-14 15:06:59.178 15290-15290/com.zs.zs_jetpack W/System.err: at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:231) 2021-05-14 15:06:59.178 15290-15290/com.zs.zs_jetpack W/System.err: at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:213) 2021-05-14 15:06:59.179 15290-15290/com.zs.zs_jetpack W/System.err: at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:436) 2021-05-14 15:06:59.179 15290-15290/com.zs.zs_jetpack W/System.err: at java.net.Socket.connect(Socket.java:621) 2021-05-14 15:06:59.179 15290-15290/com.zs.zs_jetpack W/System.err: at okhttp3.internal.platform.AndroidPlatform.connectSocket(AndroidPlatform.kt:63) 2021-05-14 15:06:59.179 15290-15290/com.zs.zs_jetpack W/System.err: at okhttp3.internal.connection.RealConnection.connectSocket(RealConnection.kt:295) 2021-05-14 15:06:59.179 15290-15290/com.zs.zs_jetpack W/System.err: at okhttp3.internal.connection.RealConnection.connect(RealConnection.kt:207) 2021-05-14 15:06:59.179 15290-15290/com.zs.zs_jetpack W/System.err: at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:226) 2021-05-14 15:06:59.179 15290-15290/com.zs.zs_jetpack W/System.err: at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:106) 2021-05-14 15:06:59.180 15290-15290/com.zs.zs_jetpack W/System.err: at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:74) 2021-05-14 15:06:59.180 15290-15290/com.zs.zs_jetpack W/System.err: at okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.kt:255) 2021-05-14 15:06:59.180 15290-15290/com.zs.zs_jetpack W/System.err: at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:32) 2021-05-14 15:06:59.180 15290-15290/com.zs.zs_jetpack W/System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) 2021-05-14 15:06:59.180 15290-15290/com.zs.zs_jetpack W/System.err: at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95) 2021-05-14 15:06:59.180 15290-15290/com.zs.zs_jetpack W/System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) 2021-05-14 15:06:59.181 15290-15290/com.zs.zs_jetpack W/System.err: at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83) 2021-05-14 15:06:59.181 15290-15290/com.zs.zs_jetpack W/System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) 2021-05-14 15:06:59.181 15290-15290/com.zs.zs_jetpack W/System.err: at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76) 2021-05-14 15:06:59.181 15290-15290/com.zs.zs_jetpack W/System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) 2021-05-14 15:06:59.181 15290-15290/com.zs.zs_jetpack W/System.err: at com.zs.base_library.http.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:75) 2021-05-14 15:06:59.181 15290-15290/com.zs.zs_jetpack W/System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109) 2021-05-14 15:06:59.182 15290-15290/com.zs.zs_jetpack W/System.err: at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201) 2021-05-14 15:06:59.182 15290-15290/com.zs.zs_jetpack W/System.err: at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:517) 2021-05-14 15:06:59.182 15290-15290/com.zs.zs_jetpack W/System.err: at java.util.concurrent.ThreadPoolExecutor.processTask(ThreadPoolExecutor.java:1187) 2021-05-14 15:06:59.182 15290-15290/com.zs.zs_jetpack W/System.err: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1152) 2021-05-14 15:06:59.182 15290-15290/com.zs.zs_jetpack W/System.err: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641) 2021-05-14 15:06:59.182 15290-15290/com.zs.zs_jetpack W/System.err: at java.lang.Thread.run(Thread.java:784)

    opened by Bingoliallen 1
  • 对getInternal的引用不明确

    对getInternal的引用不明确

    编译的时候,出现下面的错误:

    Jetpack-Mvvm/app/build/generated/source/kapt/debug/com/zs/zs_jetpack/databinding/FragmentMineBindingImpl.java:193: 错误: 对getInternal的引用不明确
                            vmInternal = vm.getInternal();
                                           ^
      MineVM 中的方法 getInternal() 和 MineVM 中的方法 getInternal() 都匹配
    

    请问该怎么解决呢?

    opened by ilongshan 4
  • 关于BaseRepo和BaseViewModel中launch函数的封装

    关于BaseRepo和BaseViewModel中launch函数的封装

    首先赞一下大佬掘金的两篇高质量文章。 看了下代码,对launch{}这个封装 有几个小建议: 一是 Dispatcher建议手动传入,这样使用者更明确block: suspend () -> T的执行环境 二是 success和error这两个callback的线程环境建议统一下,比如要么与block: suspend () -> T 相同,要么统一切换到主线程。 三是

    onFailure {
                    it.printStackTrace()
                    getApiException(it).apply {
                        error?.invoke(this)
                        toast(errorMessage)
                        //统一响应错误信息
                        errorLiveData.value = this
                    }
                }
    

    这里 toast(errorMessage) errorLiveData.value = this 可能有bug哈 建议用withContext(Dispatchers.Main.immediate) 包一下

    opened by QuincyJiang 4
  • Demo 无法运行

    Demo 无法运行

    Build file 'E:..\Jetpack-Mvvm\app\build.gradle' line: 7

    A problem occurred evaluating project ':app'.

    Could not get unknown property 'Version' for object of type com.android.build.gradle.internal.dsl.BaseAppModuleExtension.

    opened by zqlq4ever 2
Releases(1.0)
Owner
Bezier
~~~
Bezier