Jorgen's blog Jorgen's blog
首页
  • 平台架构
  • 混合式开发记录
  • 推送服务
  • 数据分析
  • 实时调度
  • 架构思想

    • 分布式
  • 编程框架工具

    • 编程语言
    • 框架
    • 开发工具
  • 数据存储与处理

    • 数据库
    • 大数据
  • 消息、缓存与搜索

    • 消息队列
    • 搜索与日志分析
  • 前端与跨端开发

    • 前端技术
    • Android
  • 系统与运维

    • 操作系统
    • 容器化与 DevOps
  • 物联网与安全

    • 通信协议
    • 安全
    • 云平台
newland
  • 关于我
  • 终身学习
  • 关于时间的感悟
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

jorgen

Love it, make mistakes, learn, keep grinding.
首页
  • 平台架构
  • 混合式开发记录
  • 推送服务
  • 数据分析
  • 实时调度
  • 架构思想

    • 分布式
  • 编程框架工具

    • 编程语言
    • 框架
    • 开发工具
  • 数据存储与处理

    • 数据库
    • 大数据
  • 消息、缓存与搜索

    • 消息队列
    • 搜索与日志分析
  • 前端与跨端开发

    • 前端技术
    • Android
  • 系统与运维

    • 操作系统
    • 容器化与 DevOps
  • 物联网与安全

    • 通信协议
    • 安全
    • 云平台
newland
  • 关于我
  • 终身学习
  • 关于时间的感悟
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Capacitor浅析
  • ADB调试
  • mlkit
  • face detection
  • Android中的Kotlin协程:告别回调地狱
  • Android性能优化实践与技巧
  • Android Jetpack Compose入门:构建现代UI的全新方式
  • Android Jetpack Compose入门与实践 - 构建现代Android UI
  • Android Jetpack Compose入门指南 - 构建现代UI的全新方式
  • Android Jetpack与架构组件-构建现代化应用
    • 前言
    • 什么是Android Jetpack?
    • 核心架构组件
      • ViewModel
      • LiveData
      • Room持久化库
      • Data Binding
      • Navigation Component
    • MVVM架构模式
    • 实战案例:用户列表应用
      • 1. 添加依赖
      • 2. 创建数据模型
      • 3. 创建Repository
      • 4. 创建ViewModel
      • 5. 创建UI
      • 6. 在Activity中绑定ViewModel
    • 最佳实践
    • 常见问题与解决方案
      • 问题1:ViewModel不更新UI
      • 问题2:Room数据库查询在主线程执行
    • 结语
  • Android Jetpack架构组件:ViewModel与LiveData实战指南
  • Android Jetpack组件:构建现代Android应用的核心利器
  • Android Jetpack组件详解:构建现代化应用的核心
  • Android数据绑定与MVVM架构-构建现代化应用
  • Android架构组件:构建现代化应用的基石
  • Android架构组件与Jetpack-构建现代化应用的基石
  • Android架构组件与Jetpack-构建现代化应用的基础
  • Android架构设计之MVVM模式实战指南
  • 拥抱未来:Android Jetpack Compose入门指南
  • Android权限管理完全指南-从基础到实践
  • Android测试指南-从单元测试到UI测试的实践之路
  • Android依赖注入实战:从Dagger到Hilt的进化之路
  • Android网络编程完全指南-从HTTP到Retrofit的实践之路
  • Android数据持久化完全指南-从SharedPreferences到Room数据库
  • Android多线程与并发处理完全指南-从Thread到协程的进阶之路
  • Android应用打包与发布全流程指南-从签名到上架的实战之路
  • Android安全完全指南-从基础防护到高级加密的实战之路
  • android
Jorgen
2023-11-15
目录

Android Jetpack与架构组件-构建现代化应用

# 前言

作为一名Android开发者,你是否曾经被Activity和Fragment的生命周期搞得晕头转向?🤯 是否曾经因为屏幕旋转导致数据丢失而抓狂?或者是在测试UI逻辑时束手无策?

我曾经也是这样,直到我发现了Android Jetpack和架构组件的强大力量!它们就像是Android开发界的"超级英雄",拯救我们于水深火热之中。😎

在这篇文章中,我将带你深入了解Android Jetpack的核心架构组件,以及如何使用它们来构建更加健壮、可测试和现代化的Android应用。

# 什么是Android Jetpack?

Android Jetpack是Google推出的官方库合集,旨在帮助开发者简化、加速Android应用开发过程。它提供了经过测试的库、工具和最佳实践,帮助开发者编写高质量的应用。

提示

Jetpack组件分为四类:基础架构、行为、界面和推荐组件。本文将重点介绍基础架构组件,因为它们是构建现代Android应用的基础。

# 核心架构组件

# ViewModel

ViewModel是Jetpack中最具代表性的组件之一。它负责为UI准备数据,并能够配置更改(如屏幕旋转)时存活下来。

class MyViewModel : ViewModel() {
    private val _data = MutableLiveData<String>()
    val data: LiveData<String> = _data

    fun loadData() {
        // 在后台加载数据
        viewModelScope.launch {
            val result = apiService.getData()
            _data.value = result
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

优点:

  • 屏幕旋转时不会丢失数据
  • 与UI控制器分离,便于测试
  • 自动管理生命周期

# LiveData

LiveData是一种可观察的数据持有者类,遵循观察者模式。当生命周期状态改变时,LiveData会通知观察者。

val user: LiveData<User> = Transformations.map(database.userDao().getUser(userId)) { 
    user -> 
    // 对数据进行转换
    user.name = "${user.name} (Updated)"
    user
}
1
2
3
4
5
6

特点:

  • 数据变化时自动更新UI
  • 生命周期感知,只在活跃状态下通知观察者
  • 防止内存泄漏

# Room持久化库

Room是一个对象映射库,提供了本地数据库功能,可以在设备上持久化应用数据。

@Entity
data class User(
    @PrimaryKey val uid: Int,
    @ColumnInfo(name = "first_name") firstName: String?,
    @ColumnInfo(name = "last_name") lastName: String?
)

@Dao
interface UserDao {
    @Query("SELECT * FROM user")
    fun getAllUsers(): List<User>
}

@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

优势:

  • 编译时SQL验证,减少运行时错误
  • 简化数据库操作
  • 支持RxJava、Flow和LiveData

# Data Binding

Data Binding库允许你将UI组件绑定到数据源,减少样板代码,提高可维护性。

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="user"
            type="com.example.User" />
    </data>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.firstName}" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.lastName}" />
    </LinearLayout>
</layout>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# Navigation Component

Navigation组件用于实现应用内的导航,简化了Fragment之间的导航逻辑。

val navController = findNavController(R.id.nav_host_fragment)
navController.navigate(R.id.action_firstFragment_to_secondFragment)
1
2

# MVVM架构模式

结合上述组件,我们可以构建现代化的MVVM(Model-View-ViewModel)架构:

MVVM架构图

  1. Model:表示数据和业务逻辑,通常包括Repository和数据源(如Room数据库)
  2. View:XML布局和Activity/Fragment,负责显示数据和用户交互
  3. ViewModel:作为View和Model之间的桥梁,处理业务逻辑并提供数据给View

MVVM的优势

  • 关注点分离:各组件职责明确
  • 可测试性:ViewModel和Model不依赖于Android框架,易于测试
  • 可维护性:代码结构清晰,便于维护和扩展

# 实战案例:用户列表应用

让我们通过一个简单的用户列表应用来实践这些组件。

# 1. 添加依赖

dependencies {
    // ViewModel
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1"
    
    // LiveData
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.6.1"
    
    // Room
    implementation "androidx.room:room-runtime:2.5.1"
    kapt "androidx.room:room-compiler:2.5.1"
    
    // Data Binding
    implementation "androidx.databinding:databinding-runtime:8.0.1"
    
    // Navigation
    implementation "androidx.navigation:navigation-fragment-ktx:2.5.3"
    implementation "androidx.navigation:navigation-ui-ktx:2.5.3"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 2. 创建数据模型

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

@Dao
interface UserDao {
    @Query("SELECT * FROM users")
    fun getAllUsers(): LiveData<List<User>>
    
    @Insert
    suspend fun insertAll(users: List<User>)
}

@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
    
    companion object {
        @Volatile
        private var INSTANCE: AppDatabase? = null
        
        fun getDatabase(context: Context): AppDatabase {
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    AppDatabase::class.java,
                    "user_database"
                ).build()
                INSTANCE = instance
                instance
            }
        }
    }
}
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
34
35
36
37

# 3. 创建Repository

class UserRepository(private val userDao: UserDao) {
    val allUsers: LiveData<List<User>> = userDao.getAllUsers()
    
    suspend fun refreshUsers() {
        // 从网络获取用户数据
        val users = apiService.getUsers()
        // 插入到数据库
        userDao.insertAll(users)
    }
}
1
2
3
4
5
6
7
8
9
10

# 4. 创建ViewModel

class UserViewModel(private val repository: UserRepository) : ViewModel() {
    val users: LiveData<List<User>> = repository.allUsers
    
    fun refreshUsers() {
        viewModelScope.launch {
            repository.refreshUsers()
        }
    }
    
    class Factory(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")
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 5. 创建UI

<!-- activity_main.xml -->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">
    
    <data>
        <variable
            name="viewModel"
            type="com.example.UserViewModel" />
    </data>
    
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
        
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/userRecyclerView"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:listData="@{viewModel.users}" />
            
        <Button
            android:id="@+id/refreshButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="刷新数据"
            android:onClick="@{() -> viewModel.refreshUsers()}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent" />
            
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
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
34
35
36
37
38

# 6. 在Activity中绑定ViewModel

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private lateinit var viewModel: UserViewModel
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        
        val database = AppDatabase.getDatabase(this)
        val repository = UserRepository(database.userDao())
        
        viewModel = ViewModelProvider(
            this,
            UserViewModel.Factory(repository)
        ).get(UserViewModel::class.java)
        
        binding.viewModel = viewModel
        binding.lifecycleOwner = this
        
        // 设置RecyclerView适配器
        binding.userRecyclerView.layoutManager = LinearLayoutManager(this)
        binding.userRecyclerView.adapter = UserAdapter()
    }
}
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

# 最佳实践

  1. 单一职责原则:每个组件只负责一项任务
  2. 依赖注入:使用Hilt或Dagger管理依赖关系
  3. 错误处理:使用try-catch块处理异常,并向用户显示适当的错误消息
  4. 测试:为ViewModel编写单元测试,为UI编写UI测试
  5. 性能优化:使用DiffUtil优化RecyclerView更新,避免不必要的UI更新

# 常见问题与解决方案

# 问题1:ViewModel不更新UI

原因:LiveData没有被正确观察,或者数据没有通过setValue()或postValue()方法更新

解决方案:

// 错误方式
_data.value = newData // 在主线程外调用会崩溃

// 正确方式
if (Looper.myLooper() == Looper.getMainLooper()) {
    _data.value = newData
} else {
    _data.postValue(newData)
}
1
2
3
4
5
6
7
8
9

# 问题2:Room数据库查询在主线程执行

原因:Room不允许在主线程执行耗时操作

解决方案:

// 使用协程在IO线程执行
viewModelScope.launch(Dispatchers.IO) {
    val users = database.userDao().getAllUsersSync()
    withContext(Dispatchers.Main) {
        // 更新UI
    }
}
1
2
3
4
5
6
7

# 结语

Android Jetpack和架构组件为我们提供了一套强大的工具,帮助我们构建更加健壮、可测试和现代化的Android应用。通过采用MVVM架构模式,我们可以实现更好的关注点分离,提高代码的可维护性和可测试性。

虽然学习这些组件需要一些时间投入,但相信我,这是值得的!我曾经也是一名"固执"的MVP架构拥护者,但自从转向Jetpack后,我再也不想回去了。😉

希望这篇文章能帮助你更好地理解和使用Android Jetpack和架构组件。如果你有任何问题或建议,欢迎在评论区留言交流!

"代码是写给人看的,顺便能在机器上运行。" —— Donald Knuth

让我们用Jetpack和架构组件构建更加出色的Android应用吧!🚀

#Android#Jetpack#架构组件#MVVM
上次更新: 2026/01/28, 10:42:53
Android Jetpack Compose入门指南 - 构建现代UI的全新方式
Android Jetpack架构组件:ViewModel与LiveData实战指南

← Android Jetpack Compose入门指南 - 构建现代UI的全新方式 Android Jetpack架构组件:ViewModel与LiveData实战指南→

最近更新
01
LLM
01-30
02
intro
01-30
03
intro
01-30
更多文章>
Theme by Vdoing | Copyright © 2019-2026 Jorgen | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式