Android Jetpack 使用指南总结
本文同步发布于公众号:移动开发那些事 Android Jetpack 使用指南总结
Jetpack
是Google
提供的一套构建高质量、高性能应用的标准工具集,可以帮忙开发者更轻松构建高质量的Android
应用。Jetpack
提供的主要组件有:
- 架构相关的:
Lifecycle
: 帮助管理生命周期感知组件,如ViewModel
和LiveData。
ViewModel
: 管理与 UI 相关的数据,确保数据在配置更改(如屏幕旋转)时保持存活。LiveData
: 一个生命周期感知的数据持有者,当数据发生变化时自动更新 UINavigation
: 简化应用内的导航,支持Fragment
和Activity
之间的跳转
- UI 相关的
Compose
: 采用声明式编程替代传统的 XML 布局
- 后台任务
WorkManager
: 简化后台任务的管理,支持持久化任务(即使应用被杀死也能继续执行),并可根据网络状态、充电状态等条件执行任务。
- 数据库
Room
:提供编译时检查 SQL 查询、简化数据库访问的注解;
- 数据存储
DataStore
: 替代SharedPreferences
,提供更好的性能和类型安全,支持协程和Flow;
1 依赖
使用前,需要在项目的build.gradle
文件中添加Jetpack
组件的依赖(需要注意整个工程的kotlin
的版本需要与Jetpack
的版本相匹配)
dependencies {
// 其他的依赖
implementation "androidx.core:core-ktx:1.7.0"
implementation "androidx.appcompat:appcompat:1.4.1"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.1"
implementation "androidx.navigation:navigation-fragment-ktx:2.4.1"
implementation "androidx.navigation:navigation-ui-ktx:2.4.1"
implementation "androidx.room:room-ktx:2.4.2"
kapt "androidx.room:room-compiler:2.4.2"
implementation "androidx.work:work-runtime-ktx:2.7.1"
// 其他的依赖 .....
}
2 架构组件
2.1 Lifecycle
Lifecycle
主要目标是帮助我们更好地管理组件的生命周期,减少内存泄漏和崩溃,同时简化代码逻辑。核心组件为:
LifecycleOwner
: 拥有生命周期的组件,如Activity
和Fragment
。它们的生命周期状态会被Lifecycle
管理 ;LifecycleObserver
: 观察者,用于监听生命周期事件并执行相应操作;
一个简单的使用示例为:
class AActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_my)
// 使用 LifecycleObserver
lifecycle.addObserver(ALifecycleObserver())
}
}
class ALifecycleObserver : DefaultLifecycleObserver {
override fun onCreate(owner: LifecycleOwner) {
super.onCreate(owner)
println("Lifecycle: onCreate")
}
// 按需要覆写其他生命周期的方法
}
Lifecycle
更多是与LiveData
和 ViewModel
集成来提供更强大的功能;
2.2 LiveData & ViewModel
LiveData
是一个可观察的数据存储器,能够感知生命周期的变化,并确保只在生命周期处于活跃状态时更新UI
,可避免常见的内存泄露问题。它一般与LifecycleOwner
绑定,能够感知到其生命周期的状态,并只在生命周期处于活跃时通知观察者(Observer
),避免了在非活跃状态下更新UI
,能减小内存泄露的风险。而且LiveData
的操作是线程安全的,观察者只会在线程上收到相关的回调通知。
而ViewModel
用于存储和管理与UI相关的数据。它的主要目的是在配置更改(如屏幕旋转、语言切换等)时保持数据的持久性,并确保数据与 UI 的分离。它的生命周期是独立于Activity
或者Fragment
的,并且与LifecycleOwner
绑定,但不持有对应的引用,从而避免内存泄露的问题,但它会在LifecycleOwner
被销毁时自动清理自身资源;
2.2.1 使用
LiveData
一般与ViewModel
结合使用,用于实现数据的持久性和生命周期的管理。
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
// 定义ViewModel
class MyViewModel : ViewModel() {
// MutableLiveData 是 LiveData 的可变版本,允许直接修改数据
private val _data = MutableLiveData<String>()
val data: LiveData<String> = _data // 外部通过 LiveData 访问,保证只读
// 修改数据的方法
fun updateData(newData: String) {
_data.value = newData
}
}
// 使用示例
// 使用 ViewModelProvider 获取 ViewModel 实例
private val viewModel: MyViewModel by viewModels();
// 观察 LiveData 数据
viewModel.data.observe(this, Observer { newData ->
// 更新 UI
textView.text = newData
})
// 数据更新
button.setOnClickListener {
viewModel.updateData("Hello, LiveData!")
}
2.2.2 LiveData数据倒灌
数据倒灌是指LiveData
会把之前已经发送过的数据再次发送给观察者,这个问题主要是由于LiveData
具备粘性事件的特性(新观察者注册时会立刻收到LiveData
的最新数据)。
如果不想发生数据倒灌的问题,可通过自定义SingleLiveEvent
来处理,它可保证每个事件只被消费一次
3 后台任务
WorkManager
它可以在应用退出后继续执行任务,并兼容不同版本的 Android
系统。适用于需要可靠执行的任务,即使应用被关闭或设备重启,任务也能继续执行,其有几个关键的类:
- Worker: 执行后台任务的类,需要继承
Worker
类并重写doWork
方法 - WorkRequest : 用于定义任务的执行方式和约束条件
- WorkManager : 用于调度任务
简单使用
// 步骤1 创建Worker
class MyWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
override fun doWork(): Result {
// 在这里执行后台任务
return Result.success()
}
}
// 步骤2 创建WorkRequest,可以创建 OneTimeWorkRequest 或 PeriodicWorkRequest (执行一次,还是定期任务)
// 创建任务的约束,可选
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresCharging(true)
.build()
val workRequest = OneTimeWorkRequest.Builder(MyWorker::class.java)
.setConstraints(constraints) // 添加任务的约束
.build()
// 步骤 3 执行任务的调度
val workManager = WorkManager.getInstance(context)
workManager.enqueue(workRequest)
4 Room
Room
是一个强大的数据库抽象层,通过声明式编程和编译时检查,简化了SQLite
数据库的操作。它支持 自动迁移、分页加载和事务管理,能够显著提升开发效率和应用性能,主要优点:
- 声明式数据库操作: 通过注解定义数据库模式,简化数据库操作
- 编译时检查:在编译时检查数据库模式,减少运行时错误
- 事务支持
- 自动迁移:从2.4.0版本开始,支持自动迁移功能,简化数据库的版本管理;
简单的使用
// 步骤1:定义实体类
// 表名为:users
@Entity(tableName = "users")
data class User(
@PrimaryKey(autoGenerate = true) val id: Int, // 主健
@ColumnInfo(name = "name") val name: String, // 列名1
@ColumnInfo(name = "age") val age: Int // 列名2
)
// 步骤2:定义数据库的操作方法
// Room 会自动实现这些方法
@Dao
interface UserDao {
// 查询
@Query("SELECT * FROM users")
fun getAll(): LiveData<List<User>>
// 插入 ,通过协程的关键字:suspend关键字来执行异步操作
@Insert
suspend fun insert(user: User)
// 更新
@Update
suspend fun update(user: User)
// 删除
@Delete
suspend fun delete(user: User)
}
// 步骤3:创建数据库版本和实体
@Database(entities = [User::class], version = 1)
// 继承自 RoomDatabase 的抽象类,用于定义数据库的配置和访问入口
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,
"app_database"
).build()
INSTANCE = instance
instance
}
}
}
}
// 步骤4 :使用数据库
val db = AppDatabase.getDatabase(context)
val userDao = db.userDao()
// 查询用户
val users = userDao.getAll()
// ....
5 数据存储
DataStore
提供了一种更现代、更安全的方式来存储键值对或类型化对象。DataStore
支持异步操作,使得数据存储和读取更加高效和灵活。
Preferences DataStore
: 用于存储键值对数据,类似于SharedPreferences
,但更加安全和高效。Proto DataStore
:用于存储类型化对象,适合存储复杂的数据结构
5.1 Preferences DataStore
简单的使用示例:
// 步骤1 :创建DataStore
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
// 步骤2: 定义KEY
val USER_NAME_KEY = stringPreferencesKey("user_name")
// 步骤3 写入数据
suspend fun saveUserName(context: Context, userName: String) {
context.dataStore.edit { preferences ->
preferences[USER_NAME_KEY] = userName
}
}
// 步骤4 读取数据,这里使用了Kotlin的Flow
val userNameFlow: Flow<String> = context.dataStore.data
.map { preferences ->
preferences[USER_NAME_KEY] ?: "default_name"
}
5.2 Proto DataStore
简单的使用示例:
// 步骤1 :使用protobuf 定义对应的数据结构,并生成对应的java类(具体的操作方法请自行google)
// 步骤2 :创建DataStore
val Context.userPreferencesStore: DataStore<UserPreferences> by lazy {
createDataStore(
fileName = "user_prefs.pb", // 这里的名字为步骤1里定义的pb文件
serializer = UserPreferencesSerializer, // 这里为一个自定义的序列化类
corruptHandler = ReplaceFileCorruptHandler { UserPreferences.getDefaultInstance() }
)
}
// 实现一个自定义的序列化类,用于实现如何去序列化这个UserPreferences的数据
// object UserPreferencesSerializer : Serializer<UserPreferences>
// 步骤3 写数据
suspend fun saveUserPreferences(context: Context, userName: String, userAge: Int) {
context.userPreferencesStore.updateData { preferences ->
preferences.toBuilder()
.setUserName(userName)
.setUserAge(userAge)
.build()
}
}
// 步骤4 读取数据
val userPreferencesFlow: Flow<UserPreferences> = context.userPreferencesStore.data
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)