数据持久化
# DataStore
# 文件结构
app/src/main/java/com/taobao/interview_tong_ad/
├── MainActivity.kt ← 修改:初始化 SettingsManager
├── data/
│ ├── local/
│ │ └── TodoPreferences.kt ← 新建:DataStore 存取(第5、6步)
│ └── model/
│ └── TodoModels.kt ← 新建:TodoItem 数据模型(第1步)
├── ui/
│ ├── pages/
│ │ └── TodoScreen.kt ← 新建:UI 页面(第1步,第8步改造)
│ └── viewmodel/
│ └── TodoViewModel.kt ← 新建:ViewModel(第7步)
└── utils/
└── SettingsManager.kt ← 新建:全局状态管理(第二节第2步)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
| 文件 | 第一节/第二节 | 作用 |
|---|---|---|
TodoModels.kt | 第1步创建 | TodoItem数据模型 + @Serializable |
TodoScreen.kt | 第1步创建,第8步改造 | UI 页面 |
TodoPreferences.kt | 第5、6步创建 | DataStore 读写 |
TodoViewModel.kt | 第7步创建 | 管理状态 + 调用 Preferences |
SettingsManager.kt | 第二节第2步创建 | 全局昵称管理(对应 AuthManager) |
MainActivity.kt | 修改已有文件 | App 启动时初始化 |
TodoModels.kt
// 数据模型
data class TodoItem(
val id: String = UUID.randomUUID().toString(),
val text: String,
val isDone: Boolean = false
)
1
2
3
4
5
6
2
3
4
5
6
TodoScreen.kt
// 样式
fun TodoScreen(viewModel: TodoViewModel = viewModel()) {
val todos by viewModel.todos.collectAsState() // ← 从 ViewModel 获取状态
var inputText by remember { mutableStateOf("") }
1
2
3
4
2
3
4
TodoPreferences.kt
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.*
import androidx.datastore.preferences.preferencesDataStore
// 顶层声明,确保全局只有一个 DataStore 实例
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "todo_store")
// 传入Context实例
class TodoPreferences(context: Context) {
private val dataStore = context.dataStore
// JSON 解析器(第8讲学的)
private val json = Json { ignoreUnknownKeys = true }
// 定义 Key:存 JSON 字符串
private val TODO_KEY = stringPreferencesKey("todo_list")
/** 保存待办列表 → 序列化成 JSON → 存入 DataStore */
suspend fun saveTodos(todos: List<TodoItem>) {
val jsonStr = json.encodeToString(ListSerializer(TodoItem.serializer()), todos)
dataStore.edit { it[TODO_KEY] = jsonStr }
}
/** 从 DataStore 读取 JSON → 反序列化成 List<TodoItem> */
suspend fun loadTodos(): List<TodoItem> {
return dataStore.data.map { prefs ->
val jsonStr = prefs[TODO_KEY] ?: ""
if (jsonStr.isEmpty()) emptyList()
else json.decodeFromString(ListSerializer(TodoItem.serializer()), jsonStr)
}.first()
}
// 新增:昵称的 Key
private val NICKNAME_KEY = stringPreferencesKey("nickname")
/** 保存昵称 */
suspend fun saveNickname(name: String) {
dataStore.edit { it[NICKNAME_KEY] = name }
}
/** 读取昵称 */
suspend fun loadNickname(): String {
return dataStore.data.map { it[NICKNAME_KEY] ?: "" }.first()
}
}
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
39
40
41
42
43
44
45
46
47
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
39
40
41
42
43
44
45
46
47
TodoViewModel.kt
// 管理数据
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel
class TodoViewModel(application: Application) : AndroidViewModel(application) {
// UI 状态:待办列表(第7讲学的 StateFlow)
private val _todos = MutableStateFlow<List<TodoItem>>(emptyList())
val todos = _todos.asStateFlow()
// DataStore 操作类
private val preferences = TodoPreferences(application)
init {
// App 启动时从 DataStore 加载待办列表
viewModelScope.launch {
_todos.value = preferences.loadTodos()
}
}
/** 保存到 DataStore */
private fun saveToDataStore(todos: List<TodoItem>) {
viewModelScope.launch {
preferences.saveTodos(todos)
}
}
/** 添加待办 */
fun addTodo(text: String) {
val newTodo = TodoItem(text = text)
val updated = _todos.value + newTodo
_todos.value = updated
saveToDataStore(updated)
}
/** 切换完成状态 */
fun toggleTodo(id: String) {
val updated = _todos.value.map {
if (it.id == id) it.copy(isDone = !it.isDone) else it
}
_todos.value = updated
saveToDataStore(updated)
}
/** 删除待办 */
fun deleteTodo(id: String) {
val updated = _todos.value.filter { it.id != id }
_todos.value = updated
saveToDataStore(updated)
}
}
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
SettingsManager.kt
// 全局使用(单例模式)用 object 单例 + StateFlow + DataStore
// APP启动的时候初始化
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
object SettingsManager {
// 全局可观察的昵称状态(第7讲学的 StateFlow)
private val _nickname = MutableStateFlow("")
val nickname = _nickname.asStateFlow()
private var preferences: TodoPreferences? = null
/** 初始化 - 从本地加载昵称(App 启动时调用) */
suspend fun init(context: Context) {
// 1. 创建 TodoPreferences
preferences = TodoPreferences(context.applicationContext)
// 2. 从 DataStore 读取保存的昵称
_nickname.value = preferences!!.loadNickname()
}
/** 设置昵称(保存到内存 + DataStore) */
suspend fun setNickname(name: String) {
// 1. 更新内存中的状态(UI 自动刷新)
_nickname.value = name
// 2. 持久化到 DataStore(App 重启后恢复)
preferences?.saveNickname(name)
}
}
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
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
MainActivity.kt
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 初始化 SettingsManager(从本地加载昵称)
// 传入上下文 this@MainActivity
runBlocking {
SettingsManager.init(this@MainActivity)
}
setContent {
InterviewTongTheme {
TodoScreen()
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# DataStore VS SharedPreferences
| 特性 | DataStore(推荐) | SharedPreferences(旧) |
|---|---|---|
| 异步 | 协程 Flow(不阻塞主线程) | 同步阻塞 |
| 线程安全 | 是 | 否 |
| 类型安全 | PreferencesKey | 字符串 Key |
| 响应式 | Flow 自动通知变化 | 需要手动刷新 |
全局唯一,用顶层委托创建
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.*
import androidx.datastore.preferences.preferencesDataStore
// 顶层声明,确保全局只有一个 DataStore 实例
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "todo_store")
1
2
3
4
5
6
2
3
4
5
6
| 代码 / 表达式 | 含义说明 |
|---|---|
by preferencesDataStore(name = "todo_store") | 委托创建单例 DataStore |
name = "todo_store" | 存储文件名,数据保存在 todo_store.preferences_pb 文件中 |
Context | 上下文 |
Context.dataStore | 添加一个扩展属性,任何有 Context 的地方都能通过 context.dataStore 访问 |
# 读取数据
# 更新数据
# Room
# DataStore VS Room
| 特性 | DataStore | Room |
|---|---|---|
| 数据类型 | 键值对(简单类型 + JSON 字符串) | 关系型(表、行、列) |
| 查询能力 | 只能按 Key 读取 | 支持 SQL 查询、排序、条件过滤 |
| 数据量 | 少量数据(配置、Token) | 大量数据(聊天记录、文章列表) |
| 复杂关系 | 不支持 | 支持多表关联 |
| 学习成本 | 低 | 中等(需学 SQL + 注解) |
| 面试通适用 | ✅ 用户信息、搜索历史 | 不需要(数据量小) |
# 文件结构
app/src/main/java/com/taobao/interview_tong_ad/
├── data/local/
│ ├── TodoEntity.kt ← @Entity:定义表
│ ├── TodoDao.kt ← @Dao:定义增删改查
│ └── TodoDatabase.kt ← @Database:数据库入口
└── ui/viewmodel/
└── TodoRoomViewModel.kt ← 用 Room 替代 DataStore
1
2
3
4
5
6
7
2
3
4
5
6
7
| 文件名 | 作用 | 步骤 |
|---|---|---|
build.gradle.kts + libs.versions.toml | 添加 Room 依赖 | 第1步 |
TodoEntity.kt | 定义 Entity — 数据表 | 第2步 |
TodoDao.kt | 定义 Dao — 增删改查 | 第3步 |
TodoDatabase.kt | 定义 Database — 数据库入口 | 第4步 |
TodoRoomViewModel.kt | 创建 ViewModel— 用 Room 替代 DataStore | 第5步 |
TodoEntity.kt
TodoDao.kt
TodoDatabase.kt
上次更新: 2026/10/07, 07:27:34