SQLiteDatabase2 - 创建、打开数据库

本篇只局限于Framework层源码,源码版本为Android 8.0

创建、打开数据库的时刻

对于Android经典的SQLiteDatabase使用场景-ContentProvider,官方例程都会建议在ContentProvider的onCreate()函数里实例化自己实现的SQLiteOpenHelper。当然如果仅仅是在App内部使用的话,是没有必要使用ContentProvider的,直接使用SQLiteOpenHelper操作数据库就可以了。
那么对应的SQLiteDatabase是在SQLiteOpenHelper中创建或者打开的吗?

NOT! NOT! NOT!

这里SQLiteOpenHelper默认使用了 lazy initialization 的技术,SQLiteOpenHelper操作的SQLiteDatabase对象其实是在第一次成功调用 getWritableDatabase() / getReadableDatabase() 时。当然如果想要强行提前SQLiteDatabase创建/打开的时间也是可以的,在SQLiteOpenHelper构造函数或者ContentProvider onCreate() 函数中调用 getWritableDatabase() / getReadableDatabase() 都是可以的。

为什么要使用lazy initialization呢

众所周知,如果在App中实现并注册了一个 ContentProvider,那么会在App的启动时创建对应的 ContentProvider实例,回调其生命周期 onCreate() 函数,然后注册进AMS。不使用 lazy initialization 技术直接在 SQLiteOpenHelper 构造函数中打开数据库的话,万一在App运行过程,又或者是系统运行过程(一个专门用来提供ContentProvider服务的App)中一直用不到这个数据库的话,相关的资源就会被浪费。同时,数据库的创建、打开、升级是一项耗时操作,如果放在 ContentProvider的 onCreate() 函数里面去做还会拖慢App的启动速度,这些都是一个高性能App需要避免的问题。
因此,在大多数情况下,都不建议App改变创建、打开SQLiteDatabase的时机。除非App在启动时设置了专门的 warm-up 时间段,出于追求更好性能的目标,建议你在 warm-up 阶段执行SQLiteOpenHelper创建、打开SQLiteDatabase的操作。

创建、打开数据库的流程

无论是 getWritableDatabase() 还是 getReadableDatabase(), 最后都是进行到 getDatabaseLocked(),并且是加锁调用。通过源码看看流程。

SQLiteOpenHelper.java

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
private SQLiteDatabase getDatabaseLocked(boolean writable) {
//已经有缓存了,直接返回。
if (mDatabase != null) {
if (!mDatabase.isOpen()) {
// Darn! The user closed the database by calling mDatabase.close().
mDatabase = null;
} else if (!writable || !mDatabase.isReadOnly()) {
// The database is already open for business.
return mDatabase;
}
}
//创建、打开、升级数据库是耗时操作,所以我们需要阻断执行完成前的调用
if (mIsInitializing) {
throw new IllegalStateException("getDatabase called recursively");
}

SQLiteDatabase db = mDatabase;
try {
mIsInitializing = true;
if (db != null) {
//已经创建过,reopen
if (writable && db.isReadOnly()) {
db.reopenReadWrite();
}
} else if (mName == null) {
// in-memory DB.
db = SQLiteDatabase.create(null);
} else {
// 创建一个新的DB
try {
if (DEBUG_STRICT_READONLY && !writable) {
// read-only DB
//@@ key: 获取db文件所在的绝对路径
final String path = mContext.getDatabasePath(mName).getPath();
db = SQLiteDatabase.openDatabase(path, mFactory,
SQLiteDatabase.OPEN_READONLY, mErrorHandler);
} else {
// writable DB
//@@ key: mName, 传进来的.db filename, 也是数据库所在的位置指定
db = mContext.openOrCreateDatabase(mName, mEnableWriteAheadLogging ?
Context.MODE_ENABLE_WRITE_AHEAD_LOGGING : 0,
mFactory, mErrorHandler);
}
} catch (SQLiteException ex) {
if (writable) {
throw ex;
}
Log.e(TAG, "Couldn't open " + mName
+ " for writing (will try read-only):", ex);
//打开writable DB出错,重新尝试以read-only 方法打开
final String path = mContext.getDatabasePath(mName).getPath();
db = SQLiteDatabase.openDatabase(path, mFactory,
SQLiteDatabase.OPEN_READONLY, mErrorHandler);
}
}
//配置刚刚打开的DB。(由子类来实现)
onConfigure(db);

final int version = db.getVersion();
if (version != mNewVersion) {
//不能升级read-only的数据库
if (db.isReadOnly()) {
throw new SQLiteException("Can't upgrade read-only database from version " +
db.getVersion() + " to " + mNewVersion + ": " + mName);
}
//小于最低支持的版本,直接删除重新创建一个.
if (version > 0 && version < mMinimumSupportedVersion) {
File databaseFile = new File(db.getPath());
//提供回调
onBeforeDelete(db);
db.close();
if (SQLiteDatabase.deleteDatabase(databaseFile)) {
//删除成功,重新获取一个
mIsInitializing = false;
return getDatabaseLocked(writable);
} else {
throw new IllegalStateException("Unable to delete obsolete database "
+ mName + " with version " + version);
}
} else {
//根据版本进行操作
db.beginTransaction();
try {
if (version == 0) {
//版本=0, 第一次创建的数据库
onCreate(db);
} else {
//数据库已经被降级
if (version > mNewVersion) {
onDowngrade(db, version, mNewVersion);
} else {
//升级
onUpgrade(db, version, mNewVersion);
}
}
db.setVersion(mNewVersion);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
}
//回调并记录
onOpen(db);
if (db.isReadOnly()) {
Log.w(TAG, "Opened " + mName + " in read-only mode");
}

mDatabase = db;
return db;
} finally {
mIsInitializing = false;
if (db != null && db != mDatabase) {
db.close();
}
}
}

这里有几点需要重点关注一下,虽然很多工程师对这些概念已经耳熟能详.

  • reopenReadWrite() 可以将一个已经存在的 Read-only DB 更新成可写的。
  • 使用SQLiteDatabase.create(null) 可以创建一个 in-memory DB。
  • Read-only 数据库路径的获取实现在 ContextImpl 中;可写数据库的数据库路径就直接是 mName
  • 如果以 Writable 的方法打开数据库出错,会尝试重新以 Read-only 的方式去打开一次。
  • 如果操作数据库的版本小于 本SQLiteOpenHelper支持的最小版本(在构造器中传入,默认0),会将数据库删除,然后重新创建一个。

获取Writable DB 的 SQLiteDatabase.openOrCreateDatabase()最后到转接到了 SQLiteDatabase.openDatabase,在里面先新创建一个SQLiteDatabase对象,然后执行它的open() 操作。

SQLiteDatabase.java

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

public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory,
DatabaseErrorHandler errorHandler) {
return openDatabase(path, factory, CREATE_IF_NECESSARY, errorHandler);
}


public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags,
DatabaseErrorHandler errorHandler) {
SQLiteDatabase db = new SQLiteDatabase(path, flags, factory, errorHandler);
db.open();
return db;
}


private void open() {
try {
try {
//根据在构造器中传入的配置打开,第一次尝试
openInner();
} catch (SQLiteDatabaseCorruptException ex) {
//失败之后先进行错误通知,再重试一次。
onCorruption();
openInner();
}
} catch (SQLiteException ex) {
Log.e(TAG, "Failed to open database '" + getLabel() + "'.", ex);
close();
throw ex;
}
}

private void openInner() {
synchronized (mLock) {
assert mConnectionPoolLocked == null;
mConnectionPoolLocked = SQLiteConnectionPool.open(mConfigurationLocked);
mCloseGuardLocked.open("close");
}

synchronized (sActiveDatabases) {
sActiveDatabases.put(this, null);
}
}

之后的open操作就会转移到SQLiteConnectionPool里面执行;如果执行成功,则会返回一个数据库连接池SQLiteConnectionPool,记录在mConnectionPoolLocked中以待后面使用。
再往下就是SQLiteConnection如何执行open() ,SQLiteConnection是个JNI方法的包装类。因此,对App工程师来说了解到SQLiteOpenHelped的数据库操作最后都是由SQLiteConnectionPool和其中管理的SQLiteConnection来完成就足够了。