SQLite 是一个轻量级的关系型数据库引擎,Android 内置对其支持,非常适合本地数据持久化。无需额外配置,通过 android.database.sqlite 包下的 API 即可使用。

SQLiteOpenHelper 的基本用法

创建和打开数据库只需继承 SQLiteOpenHelper。构造方法中指定数据库名和版本号,系统会自动判断:如果数据库已存在则打开,否则创建新库并回调 onCreate

 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
public class DBHelper extends SQLiteOpenHelper {
    private static final String TAG = "DBHelper";
    private static final String DATABASE_NAME = "contacts.db";
    private static final int DATABASE_VERSION = 1;

    public DBHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    /**
     * Called when the database is created for the first time.
     */
    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        Log.i(TAG, "[" + DATABASE_NAME + " v." + DATABASE_VERSION + "]");
        // TODO: Create tables here
    }

    /**
     * Called when the DATABASE_VERSION is increased.
     */
    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase,
                          int oldVersion, int newVersion) {
        Log.i(TAG, "Upgrading database[" + DATABASE_NAME +
              " v." + newVersion + "]");
    }
}

需要注意的是,数据库直到首次调用 getWritableDatabase()getReadableDatabase() 时才会真正创建或打开。onUpgrade 在版本号递增时触发,应在此处执行表结构变更逻辑。

单例模式与线程安全

SQLiteOpenHelper 的子类会返回同一个 SQLiteDatabase 实例。这意味着在任何线程调用 close() 都会关闭应用中所有的数据库实例。因此需要格外注意打开和关闭的时机。

建议的实践:

  • 使用单例模式管理 SQLiteOpenHelper 实例
  • 避免在多处频繁开关数据库连接
  • 除非数据量特别大,否则尽量将数据合并到一个数据库中,避免多个 SQLiteOpenHelper 实例带来的混乱

数据库文件的存储位置与安全性

数据库文件对应用来说是私有的,默认存储在 /data/data/(packageName)/database/ 路径下。但需要注意:

  • 数据库文件没有加密,Root 过的设备上任何人都可以直接读取
  • 如需在非 Root 设备上导出数据库文件,需要在应用中先将其复制到公共目录
  • 敏感数据建议使用 EncryptedDatabase 或 SQLCipher 等方案加密

参考