一、项目简介
-
项目采用 Kotlin 语言编写,结合
Jetpack
相关控件,Navigation
,Lifecyle
,DataBinding
,LiveData
,ViewModel
等搭建的 MVVM 架构模式; -
通过组件化 ,模块化拆分,实现项目更好解耦和复用 实现模块间通信;
-
通过
Eventbus
实现各个组件之间的信息传递 -
通过
service
实现音乐的播放 -
通过
Bmob
,Room
数据库等实现对数据缓存的管理; -
使用
Glide
完成图片加载; -
项目截图:
github afbasfh/NeteaseMusic (github.com)
二、项目详情
2.1 基础架构
(1) BaseActicity
通过单一职责原则,实现职能分级,使用者只需要按需继承即可。
kotlin
abstract class BaseActivity:AppCompatActivity() {
private var _binding: ViewBinding?=null
val binding: ViewBinding?
get()=_binding
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
val resId=getLayoutId()
if (resId!=0)
setContentView(resId)
_binding=getLayoutBinding()
if (_binding!=null)
setContentView(_binding!!.root)
StatusBarUtil.setTransparent(this)
initUi()
}
open fun initUi(){}
/***
* 获取布局的资源Id
*/
open fun getLayoutId():Int=0
/***
* 获取绑定类对象
*/
open fun getLayoutBinding():ViewBinding?=null
}
(2) BaseFragment
BaseFragment 的封装与上面的 BaseActivity 类似。
(3) AlertView
kotlin
class LGHAlertView {
private lateinit var mContext: Context
private lateinit var mView:View
private lateinit var mWindowManager:WindowManager
private lateinit var rootContainer:FrameLayout
constructor(context: Context,resId:Int){
mContext=context
mView= LayoutInflater.from(context).inflate(resId, null)
mWindowManager =mContext.getSystemService(Context.WINDOW_SERVICE) as WindowManager
rootContainer= FrameLayout(mContext)
}
constructor(context: Context,view: View){
mContext=context
mView=view
mWindowManager =mContext.getSystemService(Context.WINDOW_SERVICE) as WindowManager
rootContainer= FrameLayout(mContext)
}
fun show(position:Positon=Positon.CENTER,autoDismiss:Boolean=true) {
val Ip = WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_APPLICATION_PANEL,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT
)
Ip.gravity = when (position) {
Positon.TOP -> Gravity.CENTER_HORIZONTAL or Gravity.TOP
Positon.CENTER -> Gravity.CENTER_HORIZONTAL or Gravity.CENTER_VERTICAL
Positon.BOTTON -> Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM
}
rootContainer.addView(mView)
mWindowManager.addView(rootContainer, Ip)
mView.startAnimation(AnimUtils.SlideInFromTopAnimation())
if (autoDismiss == true) {
rootContainer.postDelayed({
mView.startAnimation((AnimUtils.SlideOutFromTopAnimation {
dismiss()
}))
}, 2000)
}
}
fun dismiss() {
if (rootContainer.parent != null) {
mWindowManager.removeView(rootContainer)
}
}
enum class Positon {
CENTER, TOP, BOTTON
}
}
信息弹框的封装,使用时只需要传递视图对应的view或者ResID
2.2 MVVM(Model-View-ViewModel)
是一种基于数据绑定的架构模式,用于设计和组织应用程序的代码结构。它将应用程序分为三个主要部分:Model(模型)、View(视图)和ViewModel(视图模型)。
- Model(模型):负责处理数据和业务逻辑。它可以是从网络获取的数据、数据库中的数据或其他数据源。Model层通常是独立于界面的,可以在多个界面之间共享。
- View(视图):负责展示数据和与用户进行交互。它可以是Activity、Fragment、View等。View层主要负责UI的展示和用户输入的响应。
- ViewModel(视图模型):连接View和Model,作为View和Model之间的桥梁。它负责从Model中获取数据,并将数据转换为View层可以直接使用的形式。ViewModel还负责监听Model的数据变化,并通知View进行更新。ViewModel通常是与View一一对应的,每个View都有一个对应的ViewModel。
2.3 Jetpack组件
(1) Navtgation
Google 在2018年推出了 Android Jetpack,在Jetpack里有一种管理fragment的新架构模式,那就是navigation. 字面意思是导航,但是除了做APP引导页面以外.也可以使用在App主页分tab的情况.. 甚至可以一个功能模块就一个activity大部分页面UI都使用fragment来实现,而navigation就成了管理fragment至关重要的架构.
这里主要用于页面的切换
(2) ViewBinding&DataBinding
ViewBinding
的出现就是不再需要写findViewById()
;DataBinding
是一种工具,它解决了 View 和数据之间的双向绑定;减少代码模板 ,不再需要写findViewById()
;释放 Activity/Fragment ,可以在 XML 中完成数据,事件绑定工作,让Activity/Fragment
更加关心核心业务;数据绑定空安全 ,在 XML 中绑定数据它是空安全的,因为DataBinding
在数据绑定上会自动装箱和空判断,所以大大减少了 NPE 问题。
(3) ViewModel
ViewModel
具备生命感知能力的数据存储组件。页面配置更改数据不会丢失,数据共享(单 Activity 多 Fragment 场景下的数据共享),以生命周期的方式管理界面相关的数据,通常和 DataBinding 配合使用,为实现 MVVM 架构提供了强有力的支持。
(4) LiveData
LiveData
是一个具有生命周期感知能力的数据订阅,分发组件。支持共享资源(一个数据支持被多个观察者接收的),支持粘性事件的分发,不再需要手动处理生命周期(和宿主生命周期自动关联),确保界面符合数据状态。在底层数据库更改时通知 View。
(5) Room
一个轻量级 orm 数据库,本质上是一个 SQLite 抽象层 。使用更加简单(Builder 模式,类似 Retrofit),通过注解的形式实现相关功能,编译时自动生成实现类 IMPL。
这里主要用于收藏点赞音乐,与 LiveData
和 Flow 结合处理可以避免不必要的 NPE,可以监听数据库表中的数据的变化,也可以和 RXJava 的 Observer 使用,一旦发生了 insert,update,delete等操作,Room
会自动读取表中最新的数据,发送给 UI 层,刷新页面。
2.4 Bmob云数据库
主要用于音乐名称,歌手,音频资源,封面,榜单的存储 使用方法:
(1)、要是使用Bmob后台云数据库,需要先注册Bmob账号,在网址栏输入 www.bmob.cn 或者在百度输入"Bmob后端云"进行搜索,打开Bmob官网后,点击右上角的"注册",即可注册账号。
(2)、注册完成之后登录使用。要使用Bmob云数据库服务我们的应用程序,需要先创建一个应用。点击"应用Key"获取应用的应用密钥,应用密钥是连接我们程序的一串加密字符串。 (3)、导入依赖 在app
的build.gradle
文件中添加依赖文件
:
kotlin
dependencies {
implementation 'io.github.bmob:android-sdk:3.9.4'
implementation 'io.reactivex.rxjava2:rxjava:2.2.8'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation 'com.squareup.okhttp3:okhttp:4.8.1'
implementation 'com.squareup.okio:okio:2.2.2'
implementation 'com.google.code.gson:gson:2.8.5'
}
(4)、 创建子类
新建一个继承自Application
的子类BmobApp
。代码如下
kotlin
public class BmobApp extends Application { @Override public void onCreate()
{ super.onCreate();
Bmob.initialize(this, "你的application id");
}
}
(5)、配置AndroidManifest.xml
在你的应用程序的AndroidManifest.xml
文件中添加如下的应用类名
、权限
和ContentProvider
信息
kotlin
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="cn.bmob.example" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17"/><!--允许联网 -->
<uses-permission android:name="android.permission.INTERNET" /> <!--获取GSM(2g)、WCDMA(联通3g)等网络状态的信息 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!--获取wifi网络状态的信息 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!--获取sd卡写的权限,用于文件上传和下载-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!--允许读取手机状态 用于创建BmobInstallation-->
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> <application android:name=".BmobApp" ....其他信息>
<activity ...其他信息 </activity>
<!--添加ContentProvider信息 -->
<provider android:name="cn.bmob.v3.util.BmobContentProvider" android:authorities="你的应用包名.BmobContentProvider">
</provider> </application> </manifest>
具体操作请浏览数据存储 · Android -- Bmob后端云 (bmobapp.com)
2.5 图片加载库
(1)、导入依赖
kotlin
implementation 'com.github.bumptech.glide:glide:4.16.0'
implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.0"))
(2)、使用
kotlin
Glide.with(this)
.load(it.url)
.into(binding.ivAdvertiesment)
2.6 Eventbus
EventBus是针一款对Android的发布/订阅事件总线。它可以让我们很轻松的实现在Android各个组件之间传递消息,并且代码的可读性更好,耦合度更低 使用方法:
(1)定义事件对象
事件对象可以是任意java类型,没有特殊要求,比如String、int、自定义类等。
kotlin
sealed class AudioEvents
//音频开始加载的事件
class AudioLoadEvent(val music:Song):AudioEvents()
//开始播放事件
object AudioStartEvent : AudioEvents()
//暂停播放事件
object AudioPauseEvent: AudioEvents()
//进度变化事件
class AudioProgressChangeEvent(val now:Int,val total:Int):AudioEvents()
//播放模式改变事件
class AudioPlayModeChangeEvent(val mode: AudioController.PlayMode):AudioEvents()
//音乐收藏和取消收藏
class AudioFavoriteStatusChangeEvent(val music: Song, val isFavorite:Boolean):AudioEvents()
(2)、在接收消息的页面注册事件
kotlin
EventBus.getDefault().post(AudioPauseEvent)
(3)、订阅者实现事件处理方法
kotlin
@Subscribe(threadMode = ThreadMode.MAIN)
fun onLoadEvent(event:AudioLoadEvent){
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onStartEvent(event: AudioStartEvent){
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onPauseEvent(event: AudioPauseEvent){
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onProgressChangeEvent(event: AudioProgressChangeEvent){
}
@SuppressLint("SetTextI18n")
private fun initLoadUi(music:Song){
}
(4)、在接收消息的页面注销(解除注册)事件
kotlin
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
// 取消订阅
EventBus.getDefault().unregister(this)
}
2.7 Service
Service是Android中实现程序后台运行的解决方案,适用于去执行那些不需要和用户交互而且还要求长期运行的任务。Service是android 系统中的四大组件之一(Activity、Service、BroadcastReceiver、ContentProvider),它跟Activity的级别差不多,但不能自己运行只能后台运行,并且可以和其他组件进行交互 。
使用方法:
1、首先创建MusicService类继承Service,并重写service的构造方法
kotlin
class MusicService: Service() {
override fun onCreate() {
super.onCreate()
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
super.onDestroy()
}
}
2、在AndroidManifest.xml中进行注册
ini
<service android:name=".service.MusicService"/>
3、具体实现
2.8 组件化&模块化
组件化:把重复的代码提取出来合并成为一个个组件,组件最重要的就是重用(复用),位于框架最底层,其他功能都依赖于组件,可供不同功能使用,独立性强。多个组件可以组合成组件库,方便调用和复用,组件间也可以嵌套,小组件组合成大组件。
模块化:分属同一功能/业务的代码进行隔离(分装)成独立的模块,可以独立运行,独立管理,每个模块有很多接口,可供调用组件化模块化优点:开发调试效率高、可维护性强、避免阻断、版本管理更容易,让代码更好维护,让多种数据分类更加明确