跳到主要内容

架构组件

Android Jetpack 提供了一套架构组件,帮助开发者构建稳健、可测试、可维护的应用。本章将介绍 ViewModel、LiveData、Lifecycle 等核心架构组件。

架构原则

在介绍具体组件之前,先了解 Android 推荐的架构原则:

分离关注点

不要将所有代码都写在 Activity 或 Fragment 中。Activity 和 Fragment 应该只负责 UI 相关的逻辑,业务逻辑和数据访问应该分离到其他类中。

通过数据模型驱动界面

使用持久化数据模型来驱动 UI,这样即使应用被系统销毁重建,数据也不会丢失。

单一数据源(SSOT)

每种数据类型应该只有一个数据源,其他地方只是对这个数据源的引用。

推荐架构

ViewModel

ViewModel 用于存储和管理与 UI 相关的数据,它在配置变更(如屏幕旋转)时不会被销毁,数据得以保留。

添加依赖

dependencies {
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.2")
implementation("androidx.activity:activity-ktx:1.8.2")
implementation("androidx.fragment:fragment-ktx:1.6.2")
}

创建 ViewModel

class MainViewModel : ViewModel() {

private val _count = MutableLiveData<Int>()
val count: LiveData<Int> = _count

init {
_count.value = 0
}

fun increment() {
_count.value = (_count.value ?: 0) + 1
}

fun decrement() {
_count.value = (_count.value ?: 0) - 1
}
}

在 Activity 中使用

class MainActivity : AppCompatActivity() {

private val viewModel: MainViewModel by viewModels()
private lateinit var binding: ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

// 观察数据变化
viewModel.count.observe(this) { count ->
binding.tvCount.text = count.toString()
}

binding.btnIncrement.setOnClickListener {
viewModel.increment()
}

binding.btnDecrement.setOnClickListener {
viewModel.decrement()
}
}
}

在 Fragment 中使用

class MainFragment : Fragment() {

// 获取 Fragment 专属的 ViewModel
private val viewModel: MainViewModel by viewModels()

// 获取 Activity 共享的 ViewModel
// private val sharedViewModel: SharedViewModel by activityViewModels()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

viewModel.count.observe(viewLifecycleOwner) { count ->
// 更新 UI
}
}
}

ViewModel 与配置变更

ViewModel 在配置变更时不会被销毁,数据得以保留:

class MainViewModel : ViewModel() {

init {
Log.d("ViewModel", "ViewModel 创建")
}

override fun onCleared() {
super.onCleared()
Log.d("ViewModel", "ViewModel 销毁")
}
}

// 屏幕旋转时:
// Activity: onDestroy -> onCreate
// ViewModel: 不会销毁,数据保留

AndroidViewModel

如果需要在 ViewModel 中访问 Application Context,使用 AndroidViewModel:

class MyViewModel(application: Application) : AndroidViewModel(application) {

fun getResourceString(resId: Int): String {
return getApplication<Application>().getString(resId)
}
}

// 使用
class MainActivity : AppCompatActivity() {
private val viewModel: MyViewModel by viewModels()
}

LiveData

LiveData 是一个可观察的数据持有者,它具有生命周期感知能力,只有在活跃状态下才会通知观察者更新。

LiveData 的特点

  • 生命周期感知:只在观察者处于活跃状态(STARTED 或 RESUMED)时才通知更新
  • 自动清理:当观察者被销毁时自动移除,避免内存泄漏
  • 数据一致性:始终保持最新数据,配置变更后自动更新

基本使用

class MainViewModel : ViewModel() {

// 私有的 MutableLiveData,用于内部修改
private val _name = MutableLiveData<String>()
// 公开的 LiveData,只读
val name: LiveData<String> = _name

fun setName(newName: String) {
_name.value = newName
}

// 使用协程更新数据
fun loadData() {
viewModelScope.launch {
val data = fetchDataFromNetwork()
_name.postValue(data) // 在后台线程中使用 postValue
}
}

private suspend fun fetchDataFromNetwork(): String {
delay(1000)
return "网络数据"
}
}

观察 LiveData

// 在 Activity 中
viewModel.name.observe(this) { name ->
binding.tvName.text = name
}

// 在 Fragment 中(使用 viewLifecycleOwner)
viewModel.name.observe(viewLifecycleOwner) { name ->
binding.tvName.text = name
}

MutableLiveData vs LiveData

  • LiveData:只读,只能通过观察获取数据
  • MutableLiveData:可读写,可以修改数据
// 推荐:对外暴露不可变的 LiveData
private val _data = MutableLiveData<String>()
val data: LiveData<String> = _data

// 内部修改
_data.value = "新数据"

Transformations

LiveData 支持数据转换:

class MainViewModel : ViewModel() {

private val _userId = MutableLiveData<Int>()
val userId: LiveData<Int> = _userId

// map 转换
val userName: LiveData<String> = Transformations.map(userId) { id ->
"用户$id"
}

// switchMap 转换(返回新的 LiveData)
val userDetail: LiveData<User> = Transformations.switchMap(userId) { id ->
repository.getUserById(id)
}

fun setUserId(id: Int) {
_userId.value = id
}
}

MediatorLiveData

合并多个 LiveData 的数据:

class MainViewModel : ViewModel() {

private val _name = MutableLiveData<String>()
val name: LiveData<String> = _name

private val _age = MutableLiveData<Int>()
val age: LiveData<Int> = _age

// 合并两个 LiveData
val userInfo: MediatorLiveData<String> = MediatorLiveData()

init {
userInfo.addSource(_name) { updateUserInfo() }
userInfo.addSource(_age) { updateUserInfo() }
}

private fun updateUserInfo() {
val name = _name.value ?: ""
val age = _age.value ?: 0
userInfo.value = "$name, $age 岁"
}
}

Lifecycle

Lifecycle 组件用于管理 Activity 和 Fragment 的生命周期,让其他组件能够感知并响应生命周期变化。

LifecycleOwner

Activity 和 Fragment 实现了 LifecycleOwner 接口,可以通过 lifecycle 属性获取生命周期对象:

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

// 获取当前生命周期状态
val currentState = lifecycle.currentState

// 添加生命周期观察者
lifecycle.addObserver(MyObserver())
}
}

LifecycleObserver

创建自定义的生命周期观察者:

class MyObserver : LifecycleObserver {

@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onStart() {
Log.d("MyObserver", "onStart")
}

@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onStop() {
Log.d("MyObserver", "onStop")
}

@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroy() {
Log.d("MyObserver", "onDestroy")
}
}

DefaultLifecycleObserver(推荐)

使用 DefaultLifecycleObserver 接口(更现代的方式):

class MyObserver : DefaultLifecycleObserver {

override fun onStart(owner: LifecycleOwner) {
Log.d("MyObserver", "onStart")
}

override fun onStop(owner: LifecycleOwner) {
Log.d("MyObserver", "onStop")
}

override fun onDestroy(owner: LifecycleOwner) {
Log.d("MyObserver", "onDestroy")
}
}

完整示例

下面是一个完整的 MVVM 架构示例:

1. 数据模型

@Entity(tableName = "users")
data class User(
@PrimaryKey val id: Int,
val name: String,
val email: String
)

2. 数据访问对象

@Dao
interface UserDao {
@Query("SELECT * FROM users")
fun getAll(): Flow<List<User>>

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(user: User)
}

3. Repository

class UserRepository(
private val userDao: UserDao,
private val apiService: ApiService
) {
fun getUsers(): Flow<List<User>> = userDao.getAll()

suspend fun refreshUsers() {
val users = apiService.getUsers()
users.forEach { userDao.insert(it) }
}
}

4. ViewModel

class UserViewModel(
private val repository: UserRepository
) : ViewModel() {

private val _isLoading = MutableStateFlow(false)
val isLoading: StateFlow<Boolean> = _isLoading

private val _error = MutableSharedFlow<String>()
val error: SharedFlow<String> = _error

val users: Flow<List<User>> = repository.getUsers()

fun refresh() {
viewModelScope.launch {
_isLoading.value = true
try {
repository.refreshUsers()
} catch (e: Exception) {
_error.emit(e.message ?: "加载失败")
} finally {
_isLoading.value = false
}
}
}
}

class UserViewModelFactory(
private val repository: UserRepository
) : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(UserViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return UserViewModel(repository) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}

5. Activity

class MainActivity : AppCompatActivity() {

private val viewModel: UserViewModel by viewModels {
UserViewModelFactory((application as MyApp).repository)
}
private lateinit var binding: ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

// 观察用户列表
lifecycleScope.launch {
viewModel.users.collect { users ->
binding.recyclerView.adapter = UserAdapter(users)
}
}

// 观察加载状态
lifecycleScope.launch {
viewModel.isLoading.collect { isLoading ->
binding.progressBar.visibility = if (isLoading) View.VISIBLE else View.GONE
}
}

// 观察错误
lifecycleScope.launch {
viewModel.error.collect { error ->
Snackbar.make(binding.root, error, Snackbar.LENGTH_SHORT).show()
}
}

// 刷新数据
binding.swipeRefresh.setOnRefreshListener {
viewModel.refresh()
}
}
}

小结

本章介绍了 Android Jetpack 架构组件:

  1. 架构原则:分离关注点、数据驱动 UI、单一数据源
  2. ViewModel:管理 UI 数据,配置变更时保留数据
  3. LiveData:生命周期感知的可观察数据持有者
  4. Lifecycle:管理生命周期,让组件感知生命周期变化
  5. 完整示例:MVVM 架构的实现

下一章将学习 Jetpack Compose,Android 现代的声明式 UI 工具包。