数据存储与访问之——又见SQLite数据库
承接之前对 SQLite 的介绍,这次我们深入探讨 SQLite 在 Android 中的高级用法、优化技巧以及与 SharedPreferences 的对比,保持简洁并聚焦实用内容。如果你对 SQLite 的基础概念(如创建数据库、基本 CRUD 操作)已有了解,以下内容将帮助你进一步掌握其高级应用。
1. 回顾 SQLite 核心特点
- 轻量级:嵌入式数据库,单文件存储,适合移动设备。
- SQL 支持:支持标准 SQL,适合结构化数据。
- 局限性:不支持高并发,适合单用户或低并发场景。
- 存储路径:Android 中通常位于
/data/data/包名/databases/
。
2. 高级用法
以下是 SQLite 在 Android 中的高级功能和实现方式:
事务管理
事务可显著提高批量操作性能,减少 I/O 开销。
val db = dbHelper.writableDatabase
db.beginTransaction()
try {
for (i in 1..1000) {
val values = ContentValues().apply {
put("username", "User$i")
put("age", 20 + i % 10)
}
db.insert("users", null, values)
}
db.setTransactionSuccessful()
} catch (e: SQLiteException) {
Log.e("SQLite", "Transaction failed: ${e.message}")
} finally {
db.endTransaction()
db.close()
}
复杂查询
支持 JOIN、子查询、聚合函数等。例如,查询年龄大于 20 的用户并按年龄排序:
val db = dbHelper.readableDatabase
val cursor = db.rawQuery(
"SELECT username, age FROM users WHERE age > ? ORDER BY age DESC",
arrayOf("20")
)
while (cursor.moveToNext()) {
val username = cursor.getString(0)
val age = cursor.getInt(1)
Log.d("SQLite", "Username: $username, Age: $age")
}
cursor.close()
db.close()
索引优化
为频繁查询的字段创建索引以提升性能。
val db = dbHelper.writableDatabase
db.execSQL("CREATE INDEX idx_username ON users(username)")
数据库升级
通过 SQLiteOpenHelper
的 onUpgrade
方法处理表结构变更。
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
if (oldVersion < 2) {
db.execSQL("ALTER TABLE users ADD COLUMN email TEXT")
}
if (oldVersion < 3) {
db.execSQL("CREATE TABLE settings (key TEXT PRIMARY KEY, value TEXT)")
}
}
数据库备份
复制数据库文件实现备份。
fun backupDatabase(context: Context, dbName: String, backupPath: String) {
val dbFile = context.getDatabasePath(dbName)
val backupFile = File(backupPath)
dbFile.copyTo(backupFile, overwrite = true)
}
3. 与 SharedPreferences 的对比
特性 | SQLite | SharedPreferences |
---|---|---|
数据类型 | 结构化数据(表、列、SQL 查询) | 键值对(基本类型、StringSet) |
存储容量 | 适合较大、复杂数据(MB 级) | 适合少量数据(KB 级) |
查询能力 | 支持复杂 SQL 查询(JOIN、聚合等) | 仅支持键值查询 |
性能 | 批量操作需事务优化,适合复杂逻辑 | 轻量、快速,适合简单读写 |
使用场景 | 用户数据、列表、离线缓存 | 用户偏好、简单配置、临时状态 |
线程安全 | 非线程安全,需手动加锁 | 非线程安全,建议单线程操作 |
复杂度 | 需手动管理表结构、SQL | API 简单,无需定义结构 |
选择建议:
- 用 SQLite:数据量较大、需要复杂查询或关系型存储(如用户列表、订单记录)。
- 用 SharedPreferences:简单键值对、用户偏好(如主题、开关状态)。
- 替代方案:考虑 Room(基于 SQLite,类型安全)或 DataStore(现代偏好存储)。
4. 优化技巧
- 批量操作:使用事务减少 I/O 开销。
- 索引:为 WHERE、ORDER BY 字段创建索引,但注意索引会增加写入时间。
- 连接池:避免频繁打开/关闭数据库,复用
SQLiteDatabase
实例。 - 预编译语句:使用
rawQuery
或compileStatement
提高查询效率。
val stmt = db.compileStatement("INSERT INTO users (username, age) VALUES (?, ?)")
stmt.bindString(1, "Alice")
stmt.bindLong(2, 25)
stmt.executeInsert()
- 分区存储:大表可按时间或类别分表存储,降低单表查询开销。
5. 注意事项
- 线程安全:SQLite 非线程安全,建议在单线程或使用锁(如
synchronized
)。 - 异常处理:捕获
SQLiteException
,处理磁盘满、权限错误等。 - 数据安全:敏感数据需加密,可结合 SQLCipher 加密 SQLite 数据库。
- 数据库大小:监控
.db
文件大小,避免过大影响性能。 - 迁移到 Room:Room 提供更现代的 API,支持 LiveData 和协程,推荐用于新项目。
6. 实际应用
- 离线缓存:如新闻列表、聊天记录。
- 数据管理:用户资料、订单历史。
- 复杂查询:如按条件过滤、统计分析。
如果需要更详细的代码示例(如 Room 迁移、SQLCipher 集成)、性能测试对比,或特定场景的 SQLite 实现,请告诉我!