Android项目架构知识点记录

开发架构就是描述视图层、逻辑层、数据层三者之间的关系

  • 视图层:用户界面,即界面的展示、以及交互事件的响应。
  • 逻辑层:为了实现系统功能而进行的必要逻辑。
  • 数据层:数据的获取和存储,含本地、server。

MVC : Model-View-Controller

  • Model:模型层,即数据模型,用于获取和存储数据。
  • View:视图层,即xml布局
  • Controller:控制层,负责业务逻辑

View层接收到用户操作事件,通知到 Controller 进行对应的逻辑处理,然后通知 Model去获取/更新数据,Model再把新的数据 通知到View更新界面。这就是一个完整 MVC 的数据流向。但在Android中,因为xml布局能力很弱,View的很多操作是在Activity/Fragment中的,而业务逻辑同样也是写在Activity/Fragment中。

所以,MVC的问题点 如下:
1.Activity/Fragment 责任不明,同时负责View、Controller,就会导致其中代码量大,不满足单一职责。
2.Model耦合View,View 的修改会导致 Controller 和 Model 都进行改动,不满足最少知识原则。

MVP : Model-View-Presenter

  • Model,模型层,即数据模型,用于获取和存储数据。
  • View,视图层,即Activity/Fragment
  • Presenter,控制层,负责业务逻辑。

MVP解决了MVC的问题:
1.View责任明确,逻辑不再写在Activity中,而是在Presenter中;
2.Model不再持有View。

View层 接收到用户操作事件,通知到Presenter,Presenter进行逻辑处理,然后通知Model更新数据,Model 把更新的数据给到Presenter,Presenter再通知到 View 更新界面。

自有项目MVP架构总结图,MVP有很多种,每个项目多多少少都会不一样

所以 MVP 有问题点 如下:
MVP解决了View层责任不明的问题,但并没有解决代码耦合的问题,View和Presenter之间相互持有。
1.会引入大量的IView、IPresenter接口,增加实现的复杂度。
2.View和Presenter相互持有,形成耦合。


MVVM : Model-View-ViewModel

Model,模型层,即数据模型,用于获取和存储数据。
View,视图,即Activity/Fragment
ViewModel,视图模型,负责业务逻辑。

注意,MVVM这里的ViewModel就是一个名称,可以理解为MVP中的Presenter。不等同于上一篇中的 ViewModel组件 ,Jetpack ViewModel组件是 对 MVVM的ViewModel 的具体实施方案

MVVM 的本质是 数据驱动,把解耦做的更彻底,viewModel不持有view 。
View 产生事件,使用 ViewModel进行逻辑处理后,通知Model更新数据,Model把更新的数据给ViewModel,ViewModel自动通知View更新界面,而不是主动调用View的方法。

Jetpack MVVM 是 MVVM 模式在 Android 开发中的一个具体实现,是 Android中 Google 官方提供并推荐的 MVVM实现方式。不仅通过数据驱动完成彻底解耦,还兼顾了 Android 页面开发中其他不可预期的错误,例如Lifecycle 能在妥善处理 页面生命周期 避免view空指针问题,ViewModel使得UI发生重建时 无需重新向后台请求数据,节省了开销,让视图重建时更快展示数据。首先,请查看下图,该图显示了所有模块应如何彼此交互:

各模块对应MVVM架构:

  • View层:Activity/Fragment
  • ViewModel层:Jetpack ViewModel + Jetpack LivaData
  • Model层:Repository仓库,包含 本地持久性数据 和 服务端数据

View层:包含了我们平时写的Activity/Fragment/布局文件等与界面相关的东西。

ViewModel层:用于持有和UI元素相关的数据,以保证这些数据在屏幕旋转时不会丢失,并且还要提供接口给View层调用以及和仓库层进行通信。

Repository层:要做的主要工作是判断调用方请求的数据应该是从本地数据源中获取还是从网络数据源中获取,并将获取到的数据返回给调用方。本地数据源可以使用数据库、SharedPreferences等持久化技术来实现,而网络数据源则通常使用Retrofit访问服务器提供的Webservice接口来实现。


举例

1
2
3
4
5
#DataCallBack.kt
interface DataCallBack<T> {
fun onSuccess(data: T)
fun onFailed()
}

1
2
3
4
5
#User.kt
class User {
var name:String = ""
var age:String = ""
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#UserRepository.kt
class UserRepository {

companion object {
lateinit var sInstance: UserRepository
@MainThread
fun get(): UserRepository {
sInstance = if (this::sInstance.isInitialized) sInstance else UserRepository()
return sInstance
}
}

fun getUserFromServer(c: DataCallBack<List<User>>) {
Thread {
Thread.sleep(2000)
val list = ArrayList<User>()
for (index in 1..10) {
val a = User()
a.name = "stew"
a.age = "30"
list.add(a)
}
c.onSuccess(list)
//存本地数据库
//saveUsersToLocal(users);
}.start()
}

//从本地数据库获取
fun getUsersFromLocal() {}
//存入本地数据库 (从服务端获取数据后可以调用)
fun saveUsersToLocal() {}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#UserListViewModel.kt
class UserListViewModel : ViewModel() {

private val userList: MutableLiveData<List<User>> by lazy {
MutableLiveData<List<User>>()
}

private val isLoading: MutableLiveData<Boolean> by lazy {
MutableLiveData<Boolean>()
}

fun getUserLiveData(): MutableLiveData<List<User>> {
return userList
}

fun getLoadingLiveData(): MutableLiveData<Boolean> {
return isLoading
}

fun getUserData() {
isLoading.value = true

UserRepository.get().getUserFromServer(object :DataCallBack<List<User>>{
override fun onSuccess(data: List<User>) {
userList.postValue(data)
isLoading.postValue(false)
}
override fun onFailed() {
isLoading.postValue(false)
}
})
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#TestUserActivity.kt
class TestUserActivity : AppCompatActivity() {

private val userModel: UserListViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user)

userModel.getUserLiveData().observe(this, {
findViewById<TextView>(R.id.user).text = it.toString()
})

userModel.getLoadingLiveData().observe(this, {
findViewById<ProgressBar>(R.id.progressBar).visibility = if (it) View.VISIBLE else View.GONE
})

userModel.getUserData()
}

}

参考文章:
https://juejin.cn/post/6921321173661777933#heading-8
https://github.com/stewForAni/Kotlin-Jetpack