Context.openOrCreateDatabase 與 SQLiteDatabase.openOrCreateDatabase本質上完成的功能都一樣,Context.openOrCreateDatabase最終是需要調用 SQLiteDatabase.openOrCreateDatabase來完成數據庫的創建的。
也就是說, SQLiteDatabase類是android上對sqlite的最底層的封裝,幾乎所有的對數據庫的操作最終都通過這個類來實現。
而Context裏面提供的方法,是用於上下文的時候創建數據庫,例如你在某個邏輯裏面創建的數據庫只是在特定的context裏面,對於數據庫的權限,交由context來管理,而這個邏輯可能是會提供給不止一個context
可以在程序上下文中用openOrCreateDatabase方法來創建和打開一個數據庫,方法如下:
private static final String DATABASE_NAME = "betty_db”;
private static final String DATABASE_TABLE = “betty_table”;
private static final String DATABASE_CREATE =
“create table “ + DATABASE_TABLE +
“ ( _id integer primary key autoincrement,” +
“column_one text not null);”;
SQLiteDatabase myDatabase;
private void createDatabase() {
myDatabase = openOrCreateDatabase(DATABASE_NAME, Context.MODE_PRIVATE, null);
myDatabase.execSQL(DATABASE_CREATE);
}
轉--------------------------------------------------------------------------------
梳理SQLiteDatabase、openOrCreateDatabase、SQLiteOpenHelper
據我所知,android創建數據庫可以通過以下方法:
一、 SQLiteDatabase.openOrCreateDatabase(file, factory):(以下都在這個SQLiteDatabase類中)
1. 一個類名+方法就是個static方法:
public static SQLiteDatabase openOrCreateDatabase(File file, CursorFactory factory) {
return openOrCreateDatabase(file.getPath(), factory);
}
2. 換了絕對路徑。
public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory) {
return openDatabase(path, factory, CREATE_IF_NECESSARY);
}
3.去調用了openDatabase方法。
public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags) {
SQLiteDatabase db = null;
try {
// Open the database.
return new SQLiteDatabase(path, factory, flags);
} catch (SQLiteDatabaseCorruptException e) {
// Try to recover from this, if we can.
// TODO: should we do this for other open failures?
Log.e(TAG, "Deleting and re-creating corrupt database " + path, e);
EventLog.writeEvent(EVENT_DB_CORRUPT, path);
new File(path).delete();
return new SQLiteDatabase(path, factory, flags);
}
}
4. 關鍵的就這行代碼:return new SQLiteDatabase(path, factory, flags);其他的是異常處理。
也就是新建了這個類,我們創建數據庫也可以這樣直接new出來。
5.現在關鍵的工作就落在SQLiteDatabase這個類的構造函數上了。
private SQLiteDatabase(String path, CursorFactory factory, int flags) {
if (path == null) {
throw new IllegalArgumentException("path should not be null");
}
mFlags = flags;
mPath = path;
mLogStats = "1".equals(android.os.SystemProperties.get("db.logstats"));
mSlowQueryThreshold = SystemProperties.getInt(LOG_SLOW_QUERIES_PROPERTY, -1);
mLeakedException = new IllegalStateException(path +
" SQLiteDatabase created and never closed");
mFactory = factory;
dbopen(mPath, mFlags); <-----------------------------------------------------------
mPrograms = new WeakHashMap<SQLiteClosable,Object>();
try {
setLocale(Locale.getDefault());
} catch (RuntimeException e) {
Log.e(TAG, "Failed to setLocale() when constructing, closing the database", e);
dbclose();
throw e;
}
}
6.起關鍵的就是 dbopen(mPath, mFlags);
private native void dbopen(String path, int flags);
7.看到這個native,就是jni咯!往下就是c++了。其中細體什麼實現open或create就不用管了。感興趣可以往下跟。
二、context.openOrCreateDatabase(name, mode, factory);
1、看android幫助是這樣描述的:
Open a new private SQLiteDatabase associated with this Context's application package.
Create the database file if it doesn't exist.
2 、看源碼沒發現細體的實現部分的代碼,發現的話分享一下給我。搜了很多源碼,只有這樣:
context.java中:
public abstract SQLiteDatabase openOrCreateDatabase(String name,int mode, CursorFactory factory);
是個抽象方法,其子類ContextWrapper.java:
public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory) {
return mBase.openOrCreateDatabase(name, mode, factory);
}
又沒見哪個子類去覆蓋這個方法;
小結:查網上一些資料也是說第二種通過context的方法,其實最終也是得通過SQLiteDatabase這個類中的方法。
我認爲也是,因爲1.context中也用到SQLiteDatabase 這個類啊,可以直接調用這個SQLiteDatabase 的方法嘛。
2.我們知道在data/data+加上這個應用的包名,就是這個應用程序存放私有數據的目錄。那麼這個應用程序
只要通過數據庫名稱就能找到其路徑。而這個SQLiteDatabase的 openOrCreateDatabase(File file, CursorFactory factory)
再到openOrCreateDatabase(String path, CursorFactory factory);由此可猜這個openOrCreateDatabase(file...)應該是提供
給context的。
(三) 剩下就是這個SQLiteOpenHelper
1. 都說這個SQLiteOpenHelper.java類是方便操作數據庫的類。
2. 那分析一下源碼
public abstract class SQLiteOpenHelper
3.所以通常我們要使用個類就得去繼承它。並去實現父類的抽象方法如:
public class DatabaseHelper extends SQLiteOpenHelper
{
public DatabaseHelper(Context context, String name, CursorFactory cursorFactory, int version)
{
super(context, name, cursorFactory, version);
}
@Override
public void onCreate(SQLiteDatabase db)
{
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
}
@Override
public void onOpen(SQLiteDatabase db)
{
super.onOpen(db);
}
}
4. 好像就這麼簡單,其實什麼也沒做。那什麼創建或打開數據庫?
5.重要的是SQLiteOpenHelper中的getWritableDatabase和getReadableDatabase方法,你會發現getReadableDatabase中調用了getWritableDatabase
6.所以只要getWritableDatabase清楚getWritableDatabase是如何實現的,就很明朗了。
看似複雜, 慢慢往下看,還有註釋(同時要清楚這方法目的就是要創建或打開數據庫)
public synchronized SQLiteDatabase getWritableDatabase() {
if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
return mDatabase; // The database is already open for business //如果已經打開了,直接返回。
}
if (mIsInitializing) {
throw new IllegalStateException("getWritableDatabase called recursively");
}
// If we have a read-only database open, someone could be using it
// (though they shouldn't), which would cause a lock to be held on
// the file, and our attempts to open the database read-write would
// fail waiting for the file lock. To prevent that, we acquire the
// lock on the read-only database, which shuts out other users.
boolean success = false;
SQLiteDatabase db = null;
if (mDatabase != null) mDatabase.lock();
try {
mIsInitializing = true;
if (mName == null) {
db = SQLiteDatabase.create(null); //以上沒多大意義,可以不管
} else {
db = mContext.openOrCreateDatabase(mName, 0, mFactory); //關鍵是這行代碼,是不是很熟悉
}
int version = db.getVersion();
if (version != mNewVersion) {
db.beginTransaction();
try {
if (version == 0) {
onCreate(db); //子類我們實現的方法(其實什麼也沒做,要想這第一次創建時做一些操作,
//就自己在子類的方法實現,如創建表)
} else {
onUpgrade(db, version, mNewVersion); //子類我們實現的方法(版本發生變化)
}
db.setVersion(mNewVersion);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
onOpen(db); //子類我們實現的方法
success = true;
return db;
} finally {
mIsInitializing = false;
if (success) {
if (mDatabase != null) {
try { mDatabase.close(); } catch (Exception e) { }
mDatabase.unlock();
}
mDatabase = db;
} else {
if (mDatabase != null) mDatabase.unlock();
if (db != null) db.close();
}
}
}