Sqlite 源碼分析 -- SQLiteOpenHelper (API 14)

使用注意事項(xiàng):

  1. getWritableDatabase() 不要放在 UI 線程,存在阻塞和操作耗時(shí)的情況;
  2. getReadableDatabase() 優(yōu)先返回 getWritableDatabase(), 發(fā)生 SQLiteException 異常后,才嘗試獲取只讀模式的數(shù)據(jù)庫(kù)
  3. getReadableDatabase() 由于調(diào)用了 getWritableDatabase(), 同樣不能放在 UI 線程

一、構(gòu)造方法

/**
 * 該方法會(huì)快速返回,并不會(huì)創(chuàng)建或打開(kāi)數(shù)據(jù)庫(kù)
 * 
 * Create a helper object to create, open, and/or manage a database.
 * This method always returns very quickly.  The database is not actually
 * created or opened until one of {@link #getWritableDatabase} or
 * {@link #getReadableDatabase} is called.
 *
 * @param context to use to open or create the database
 * @param name of the database file, or null for an in-memory database
 * @param factory to use for creating cursor objects, or null for the default
 * @param version number of the database (starting at 1); if the database is older,
 *     {@link #onUpgrade} will be used to upgrade the database; if the database is
 *     newer, {@link #onDowngrade} will be used to downgrade the database
 */
public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
    this(context, name, factory, version, new DefaultDatabaseErrorHandler());
}


public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version,
                        DatabaseErrorHandler errorHandler) {
    if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);
    if (errorHandler == null) {
        throw new IllegalArgumentException("DatabaseErrorHandler param value can't be null.");
    }

    mContext = context;
    mName = name;
    mFactory = factory;
    mNewVersion = version;
    mErrorHandler = errorHandler;
}

二、getWritableDatabase()

/**
 * 第一次調(diào)用該方法時(shí),將會(huì)打開(kāi)數(shù)據(jù)庫(kù),并會(huì)視情況調(diào)用 onCreate(), onUpgrade(), onDowngrade(), onOpen()
 *
 * 該方法的調(diào)用不要放在 UI 線程,存在阻塞和操作耗時(shí)的情況
 *
 * Create and/or open a database that will be used for reading and writing.
 * The first time this is called, the database will be opened and
 * {@link #onCreate}, {@link #onUpgrade} and/or {@link #onOpen} will be
 * called.
 *
 * <p>Once opened successfully, the database is cached, so you can
 * call this method every time you need to write to the database.
 * (Make sure to call {@link #close} when you no longer need the database.)
 * Errors such as bad permissions or a full disk may cause this method
 * to fail, but future attempts may succeed if the problem is fixed.</p>
 *
 * <p class="caution">Database upgrade may take a long time, you
 * should not call this method from the application main thread, including
 * from {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
 *
 * @throws SQLiteException if the database cannot be opened for writing
 * @return a read/write database object valid until {@link #close} is called
 */
public synchronized SQLiteDatabase getWritableDatabase() {
    if (mDatabase != null) {
        if (!mDatabase.isOpen()) {
            // 若是 mDatabase 已被關(guān)閉,則置空, 重新獲取
            // darn! the user closed the database by calling mDatabase.close()
            mDatabase = null;
        } else if (!mDatabase.isReadOnly()) {
            // mDatabase 已滿足條件,直接返回
            // The database is already open for business
            return mDatabase;
        }
    }

    if (mIsInitializing) {
        // 該方法不允許被遞歸調(diào)用
        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){
        /**
         * 應(yīng)對(duì)之前獲取的是只讀數(shù)據(jù)庫(kù)的情況
         * 等待該線程獲取到鎖對(duì)象后返回,此處可能造成阻塞
         */
        mDatabase.lock();
    }
    try {
        mIsInitializing = true;
        if (mName == null) {
            db = SQLiteDatabase.create(null);
        } else {
            /**
             * 層層跟進(jìn),最終:
             * db = new SQLiteDatabase(path, factory, flags, errorHandler, connectionNum)
             * 注意此處的 mode:0 == SQLiteDatabase.OPEN_READWRITE, 即可讀寫模式
             */
            db = mContext.openOrCreateDatabase(mName, 0, mFactory, mErrorHandler);
        }

        int version = db.getVersion();
        if (version != mNewVersion) {
            db.beginTransaction();
            try {
                if (version == 0) {
                    /**
                     * 數(shù)據(jù)庫(kù)不存在時(shí)才會(huì)調(diào)用 onCreate()
                     * 由子類實(shí)現(xiàn)數(shù)據(jù)庫(kù)的創(chuàng)建
                     */
                    onCreate(db);
                } else {
                    if (version > mNewVersion) {
                        // 需要注意降版本的情況,該方法的默認(rèn)實(shí)現(xiàn)是拋出異常
                        onDowngrade(db, version, mNewVersion);
                    } else {
                        // 由子類實(shí)現(xiàn)升級(jí)數(shù)據(jù)庫(kù)版本
                        onUpgrade(db, version, mNewVersion);
                    }
                }
                db.setVersion(mNewVersion);
                db.setTransactionSuccessful();
            } finally {
                db.endTransaction();
            }
        }
        // onOpen 為空實(shí)現(xiàn)
        onOpen(db);
        success = true;
        return db;
    } finally {
        // 注意此處對(duì) try 語(yǔ)句的用法,只有 finally 語(yǔ)句塊,沒(méi)有 catch 語(yǔ)句塊
        mIsInitializing = false;
        if (success) {
            /**
             * 成功獲取到可讀寫的數(shù)據(jù)庫(kù)
             */
            if (mDatabase != null) {
                try { mDatabase.close(); } catch (Exception e) { }
                mDatabase.unlock();
            }
            // 注意此處,把獲取到的可讀寫數(shù)據(jù)庫(kù)賦值給 mDatabase
            mDatabase = db;
        } else {
            if (mDatabase != null){
                mDatabase.unlock();
            }
            if (db != null) db.close();
        }
    }
}


三、getReadableDatabase()

/**
 * 該方法的調(diào)用不要放在 UI 線程,存在阻塞和操作耗時(shí)的情況
 * 優(yōu)先返回 getWritableDatabase()
 * getWritableDatabase() 發(fā)生 SQLiteException 異常后,才嘗試獲取只讀模式的數(shù)據(jù)庫(kù)
 *
 * Create and/or open a database.  This will be the same object returned by
 * {@link #getWritableDatabase} unless some problem, such as a full disk,
 * requires the database to be opened read-only.  In that case, a read-only
 * database object will be returned.  If the problem is fixed, a future call
 * to {@link #getWritableDatabase} may succeed, in which case the read-only
 * database object will be closed and the read/write object will be returned
 * in the future.
 *
 * <p class="caution">Like {@link #getWritableDatabase}, this method may
 * take a long time to return, so you should not call it from the
 * application main thread, including from
 * {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
 *
 * @throws SQLiteException if the database cannot be opened
 * @return a database object valid until {@link #getWritableDatabase}
 *     or {@link #close} is called.
 */
public synchronized SQLiteDatabase getReadableDatabase() {
    if (mDatabase != null) {
        if (!mDatabase.isOpen()) {
            // darn! the user closed the database by calling mDatabase.close()
            // 若是 mDatabase 已被關(guān)閉,則置空, 重新獲取
            mDatabase = null;
        } else {
            // 如果 mDatabase 未被 close(),則直接返回
            return mDatabase;  // The database is already open for business
        }
    }

    if (mIsInitializing) {
        throw new IllegalStateException("getReadableDatabase called recursively");
    }

    try {
        // 注意此處,優(yōu)先獲取可讀寫的數(shù)據(jù)庫(kù)
        return getWritableDatabase();
    } catch (SQLiteException e) {
        if (mName == null) throw e;  // Can't open a temp database read-only!
        Log.e(TAG, "Couldn't open " + mName + " for writing (will try read-only):", e);
    }

    SQLiteDatabase db = null;
    try {
        mIsInitializing = true;
        String path = mContext.getDatabasePath(mName).getPath();
        /**
         * 層層跟進(jìn),最終:
         * db = new SQLiteDatabase(path, factory, flags, errorHandler, connectionNum)
         * 注意此處的 SQLiteDatabase.OPEN_READONLY, 即只讀模式
         */
        db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY, mErrorHandler);
        if (db.getVersion() != mNewVersion) {
            throw new SQLiteException("Can't upgrade read-only database from version " +
                    db.getVersion() + " to " + mNewVersion + ": " + path);
        }
        // 該方法為空實(shí)現(xiàn)
        onOpen(db);
        Log.w(TAG, "Opened " + mName + " in read-only mode");
        mDatabase = db;
        return mDatabase;
    } finally {
        mIsInitializing = false;
        if (db != null && db != mDatabase){
            db.close();
        }
    }
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,366評(píng)論 25 708
  • 下面是我自己收集整理的Java線程相關(guān)的面試題,可以用它來(lái)好好準(zhǔn)備面試。 參考文檔:-《Java核心技術(shù) 卷一》-...
    阿呆變Geek閱讀 15,158評(píng)論 14 507
  • Java 多線程 線程和進(jìn)程的區(qū)別 線程和進(jìn)程的本質(zhì):由CPU進(jìn)行調(diào)度的并發(fā)式執(zhí)行任務(wù),多個(gè)任務(wù)被快速輪換執(zhí)行,使...
    安安zoe閱讀 2,268評(píng)論 1 18
  • 公元2015年11月28日(陰歷11月17日)父親,你走了!每一天里,我無(wú)時(shí)不再想念您!好多次夢(mèng)中見(jiàn)到您,可...
    一抹陽(yáng)光的燦爛時(shí)光閱讀 296評(píng)論 4 0
  • 吃過(guò)晚飯,小兒子悄悄的把我拉到一邊,說(shuō)‘’爸爸,李老師真的好牛‘’。我很好奇什么事讓兒子對(duì)這樣一位剛開(kāi)...
    flyboy168閱讀 528評(píng)論 0 1

友情鏈接更多精彩內(nèi)容