4. SQLite原生API使用与优化

4.1 SQLiteOpenHelper 原理与生命周期

SQLiteOpenHelper 是 Android 原生 SQLite 访问的核心辅助类,封装了数据库创建、版本升级和降级逻辑。开发阶段只需继承并实现 onCreate() 和 onUpgrade(),系统会按生命周期自动调度。

回调方法触发时机典型操作
onCreate(SQLiteDatabase db)数据库首次创建时执行 CREATE TABLE 语句
onUpgrade(db, oldVer, newVer)版本号升高ALTER TABLE / DROP + 重建
onDowngrade(db, oldVer, newVer)版本号降低(需覆写)回滚或清除数据
onConfigure(SQLiteDatabase db)getXxxDatabase() 前调用设置 WAL、外键约束等
onOpen(SQLiteDatabase db)数据库打开后日志记录、校验
实现模板:
public class MyDbHelper extends SQLiteOpenHelper {
    private static final String DB_NAME = "app.db";
    private static final int DB_VERSION = 1;

    public MyDbHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS users");
        onCreate(db);
    }
}
最佳实践:不要在 onCreate()/ onUpgrade() 中执行耗时查询或文件操作;使用单例模式持有 SQLiteOpenHelper 实例,避免重复创建。

4.2 CRUD 操作实现

CRUD 均通过 SQLiteDatabase 对象执行,优先使用参数化查询防注入。获取数据库实例时应统一使用 getWritableDatabase()(读写)或 getReadableDatabase()(只读)。

  • 增加(Insert):ContentValues 封装字段,insert() 返回新行 ID。
  • 查询(Query):query() 或 rawQuery() 返回 Cursor。
  • 更新(Update):update() 返回受影响行数。
  • 删除(Delete):delete() 返回删除行数。
// 插入
ContentValues cv = new ContentValues();
cv.put("name", "Alice");
cv.put("age", 28);
long newId = db.insert("users", null, cv);

// 查询
Cursor cursor = db.query("users", null, "age > ?", new String[]{"20"}, null, null, "age DESC");
while (cursor.moveToNext()) {
    String name = cursor.getString(cursor.getColumnIndexOrThrow("name"));
    int age = cursor.getInt(cursor.getColumnIndexOrThrow("age"));
}
cursor.close();

// 更新
ContentValues updateCv = new ContentValues();
updateCv.put("age", 29);
int rows = db.update("users", updateCv, "name = ?", new String[]{"Alice"});

// 删除
int deleted = db.delete("users", "id = ?", new String[]{"1"});

4.3 事务管理与 ACID 保障

事务保证多步操作原子性、一致性、隔离性和持久性。Android SQLiteDatabase 提供 beginTransaction()/setTransactionSuccessful()/endTransaction() 机制,支持嵌套事务(Savepoint)。

SQLiteDatabase db = dbHelper.getWritableDatabase();
try {
    db.beginTransaction();
    // 批量操作
    db.insert(...);
    db.update(...);
    db.setTransactionSuccessful();
} finally {
    db.endTransaction(); // 未调用 setSuccessful() 时会自动回滚
}
常见陷阱:事务内若抛出异常未捕获,务必确保 endTransaction() 在 finally 中执行;嵌套事务中外部事务的成功不会自动提交内部 savepoint,需显式 releaseSavepoint()。

4.4 批量插入性能调优

大量插入(如首次安装导入、数据同步)时逐条插入极其低效。优化方案对比:

方案耗时(1000条)原理
逐条 insert() + 自动提交~1200ms每 insert 一次写磁盘
事务包裹逐条插入~60ms批量提交,减少 I/O
SQLiteStatement + 事务~30ms预编译绑定,减少解析开销
WAL 模式 + 事务~20ms并行读写,降低锁竞争

推荐使用 SQLiteStatement 预编译 + 显式事务:

SQLiteDatabase db = dbHelper.getWritableDatabase();
String sql = "INSERT INTO users (name, age) VALUES (?, ?)";
db.beginTransaction();
try (SQLiteStatement stmt = db.compileStatement(sql)) {
    for (User user : userList) {
        stmt.bindString(1, user.getName());
        stmt.bindLong(2, user.getAge());
        stmt.executeInsert();
        stmt.clearBindings();
    }
    db.setTransactionSuccessful();
} finally {
    db.endTransaction();
}
调优要点:
  • 启用 WAL 模式:db.enableWriteAheadLogging()(Android P+ 或 setWriteAheadLoggingEnabled(true))。
  • 每 500~1000 条提交一次事务,避免事务过大导致内存溢出。
  • 批量前调用 db.execSQL("PRAGMA synchronous=OFF") 可进一步加速(注意数据安全性损失)。

4.5 查询游标管理

Cursor 代表查询结果集行指针,必须及时关闭以避免 CursorWindow 泄漏。管理原则:

  • 获取游标后尽快将数据读入内存(如 List),然后关闭游标。
  • 禁止跨 Activity/Fragment 传递 Cursor,改用 Parcelable 或 ContentProvider。
  • 使用 try-with-resources 或 finally 确保关闭。
Cursor cursor = null;
try {
    cursor = db.rawQuery("SELECT * FROM users WHERE age > ?", new String[]{"18"});
    while (cursor.moveToNext()) {
        // 读取数据
    }
} finally {
    if (cursor != null && !cursor.isClosed()) {
        cursor.close();
    }
}
性能陷阱:Cursor 默认使用 CursorWindow 大小为 ~2MB,一次加载过多列或超长字符串会导致 OOM;建议只查询所需字段,避免 SELECT *。若需分页查询,使用 LIMIT ? OFFSET ?。
SQLite 原生 API 操作流程图 App 启动 / 首次访问 getWritableDatabase() / getReadableDatabase() CRUD 操作 / 事务管理 insert / query / update / delete Cursor 查询 & 关闭 SQLiteStatement + 事务 DB.close() / Cursor.close() 资源释放完成 事务包裹批量操作可提升 10~40 倍性能;游标务必 in finally 关闭,避免泄漏