Android sqlit java层源码分析

【1.1】打开数据库

我们一般回调用SQLiteOpenHelper的getWritableDatabase,getReadableDatabase来打开一个可写或者只读数据库,其实都是通过getDatabaseLocked来实现,writable值不同而已,源码分析如下

 private SQLiteDatabase getDatabaseLocked(boolean writable) {
        if (mDatabase != null) {
            if (!mDatabase.isOpen()) {
                // 用户调用 mDatabase.close()关闭数据库,下面会重新打开
                mDatabase = null;
            } else if (!writable || !mDatabase.isReadOnly()) {
                // 只读或者不要求写操作,会使用已经打开的数据库
                return mDatabase;
            }
        }

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

        SQLiteDatabase db = mDatabase;
        try {
            mIsInitializing = true;

            if (db != null) {
                if (writable && db.isReadOnly()) {
                    //打开可写模式,所以getReadableDatabase可以转换成getWritableDatabase
                    db.reopenReadWrite();
                }
            } else if (mName == null) {
                //mName是数据库名字
                db = SQLiteDatabase.create(null);
            } else {
                try {
                    if (DEBUG_STRICT_READONLY && !writable) {
                        //打开可读只读数据库,
                        final String path = mContext.getDatabasePath(mName).getPath();
                        db = SQLiteDatabase.openDatabase(path, mFactory,
                                SQLiteDatabase.OPEN_READONLY, mErrorHandler);
                    } else {
                        //打开可预写数据库
                        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);
                    final String path = mContext.getDatabasePath(mName).getPath();
                    //失败再次打开只读数据库
                    db = SQLiteDatabase.openDatabase(path, mFactory,
                            SQLiteDatabase.OPEN_READONLY, mErrorHandler);
                }
            }

            onConfigure(db);

            final int version = db.getVersion();
            if (version != mNewVersion) {
                if (db.isReadOnly()) {
                    throw new SQLiteException("Can't upgrade read-only database from version " +
                            db.getVersion() + " to " + mNewVersion + ": " + mName);
                }

                //mMinimumSupportedVersion 默认是0,实例helper时可以设置
                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) {
                            //回调创建,一般我们的操作是创建表
                            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();
            }
        }
    }

小总结:数据库未关闭可以复用,所以getReadableDatabase也有可能的可写的,取决于上一次操作,打开数据库后查询版本号是否,就行升级或降级处理,或者删除数据库重头开始执行,最终才返回数据库,如果是升级数据库,时间可能比较久,不建议在主线程执行,以免造成卡顿,所以要处理好版本升级的问题。

【1.2】打开数据库,创建SQLiteDatabase实例时会创建一个SQLiteConnectionPool

    //SQLiteDatabase.java
   public static SQLiteDatabase openDatabase(@NonNull String path, @Nullable CursorFactory factory,
            @DatabaseOpenFlags int flags, @Nullable DatabaseErrorHandler errorHandler) {
        SQLiteDatabase db = new SQLiteDatabase(path, flags, factory, errorHandler, -1, -1, -1);
        db.open();
        return db;
    }

SQLiteConnectionPool

 public static SQLiteConnectionPool open(SQLiteDatabaseConfiguration configuration) {
        if (configuration == null) {
            throw new IllegalArgumentException("configuration must not be null.");
        }

        // Create the pool.
        SQLiteConnectionPool pool = new SQLiteConnectionPool(configuration);
        pool.open(); // might throw
        return pool;
    }

【1.3】插入数据库

根据传入的参数拼接sql语句,再交给SQLiteStatement处理

public long insertWithOnConflict(String table, String nullColumnHack,
            ContentValues initialValues, int conflictAlgorithm) {
        acquireReference();
        try {
            StringBuilder sql = new StringBuilder();
            sql.append("INSERT");
            //冲突模式 {"", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE "};默认无
            sql.append(CONFLICT_VALUES[conflictAlgorithm]);
            sql.append(" INTO ");
            sql.append(table);
            sql.append('(');

            Object[] bindArgs = null;
            int size = (initialValues != null && !initialValues.isEmpty())
                    ? initialValues.size() : 0;
            if (size > 0) {
                bindArgs = new Object[size];
                int i = 0;
                for (String colName : initialValues.keySet()) {
                    sql.append((i > 0) ? "," : "");
                    sql.append(colName);
                    bindArgs[i++] = initialValues.get(colName);
                }
                sql.append(')');
                sql.append(" VALUES (");
                for (i = 0; i < size; i++) {
                    sql.append((i > 0) ? ",?" : "?");
                }
            } else {
                sql.append(nullColumnHack + ") VALUES (NULL");
            }
            sql.append(')');

            //创建执行对象实体
            SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
            try {
                return statement.executeInsert();
            } finally {
                statement.close();
            }
        } finally {
            releaseReference();
        }
    }

【1.4】SQLiteStatement处理

SQLiteStatement 初始化数据,设置flag,只读或者可写,或者WAL(SQLiteStatement是SQLiteStatement的父类)

//SQLiteProgram.java 初始化参数,是否只写,字段,值
 SQLiteProgram(SQLiteDatabase db, String sql, Object[] bindArgs,
            CancellationSignal cancellationSignalForPrepare) {
        mDatabase = db;
        mSql = sql.trim();

        //判断读写模式,根据sql语句字符串判断
        int n = DatabaseUtils.getSqlStatementType(mSql);
        switch (n) {
            case DatabaseUtils.STATEMENT_BEGIN:
            case DatabaseUtils.STATEMENT_COMMIT:
            case DatabaseUtils.STATEMENT_ABORT:
                mReadOnly = false;
                mColumnNames = EMPTY_STRING_ARRAY;
                mNumParameters = 0;
                break;

            default:
                boolean assumeReadOnly = (n == DatabaseUtils.STATEMENT_SELECT);
                SQLiteStatementInfo info = new SQLiteStatementInfo();
                db.getThreadSession().prepare(mSql,
                        db.getThreadDefaultConnectionFlags(assumeReadOnly),
                        cancellationSignalForPrepare, info);
                mReadOnly = info.readOnly;
                mColumnNames = info.columnNames;
                mNumParameters = info.numParameters;
                break;
        }
//  SQLiteDatabase.java 设置flag
   int getThreadDefaultConnectionFlags(boolean readOnly) {
        int flags = readOnly ? SQLiteConnectionPool.CONNECTION_FLAG_READ_ONLY :
                SQLiteConnectionPool.CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY;
        if (isMainThread()) {
            flags |= SQLiteConnectionPool.CONNECTION_FLAG_INTERACTIVE;
        }
        return flags;
    }

SQLiteSession 最终执行

//SQLiteDatabase.java 
  SQLiteSession getThreadSession() {
  //每个线程持不同Session对象
        return mThreadSession.get(); // initialValue() throws if database closed
    }

【1.5】获取Session,执行sql

Session保存在ThreadLocal,所以每个thread都有自己的Session实例

  public long executeInsert() {
        acquireReference();
        try {
            return getSession().executeForLastInsertedRowId(
                    getSql(), getBindArgs(), getConnectionFlags(), null);
        } catch (SQLiteDatabaseCorruptException ex) {
            onCorruption();
            throw ex;
        } finally {
            releaseReference();
        }
    }

【1.6】获取ConnectionPool中的Connection

SQLiteConnection naviti方法封装,真正执行sql方法

   // SQLiteSession.java 获取SQLiteConnection对象
  private void acquireConnectionSQLiteConnection(String sql, int connectionFlags,
            CancellationSignal cancellationSignal) {
        if (mConnection == null) {
            assert mConnectionUseCount == 0;
            mConnection = mConnectionPool.acquireConnection(sql, connectionFlags,
                    cancellationSignal); // might throw
            mConnectionFlags = connectionFlags;
        }
        mConnectionUseCount += 1;
    }

【1.7】请求获取Connection

使用了lock,未开启WAL只能同时执行一个读或写操作,mMaxConnectionPoolSize=1,只有mAvailablePrimaryConnection主连接,打开WAL,可同时执行多个读操作,但写只能同时存在一个,读写可以并行,

//SQLiteConnectionPool.java
public SQLiteConnection acquireConnection(String sql, int connectionFlags,
            CancellationSignal cancellationSignal) {
        SQLiteConnection con = waitForConnection(sql, connectionFlags, cancellationSignal);
        synchronized (mLock) {
            if (mIdleConnectionHandler != null) {
                mIdleConnectionHandler.connectionAcquired(con);
            }
        }
        return con;
    }


private SQLiteConnection waitForConnection(String sql, int connectionFlags,
            CancellationSignal cancellationSignal) {
        //是否获取主链接,只读操作为false,查看上面的getThreadDefaultConnectionFlags方法
            final boolean wantPrimaryConnection =
                (connectionFlags & CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY) != 0;
            ......
      // Try to acquire a connection.
            SQLiteConnection connection = null;
            if (!wantPrimaryConnection) {
            // 从连接列表获取,写操作不从这里获取
            // private final ArrayList mAvailableNonPrimaryConnections = new ArrayList();
                connection = tryAcquireNonPrimaryConnectionLocked(
                        sql, connectionFlags); // might throw
            }
            if (connection == null) {
            // 从主连接获取
            //private SQLiteConnection mAvailablePrimaryConnection;
                connection = tryAcquirePrimaryConnectionLocked(connectionFlags); // might throw
            }
            if (connection != null) {
                return connection;
            }

【1.8】打开WAL

打开WAL可以增加连接数,SQLiteDatabase.enableWriteAheadLogging disableWriteAheadLogging,可打开或关闭读写并行,打开后内存消耗也会增加, 下面为enableWriteAheadLogging的注释

     * When write-ahead logging is not enabled (the default), it is not possible for
     * reads and writes to occur on the database at the same time.  Before modifying the
     * database, the writer implicitly acquires an exclusive lock on the database which
     * prevents readers from accessing the database until the write is completed.

     * It is a good idea to enable write-ahead logging whenever a database will be
     * concurrently accessed and modified by multiple threads at the same time.
     * However, write-ahead logging uses significantly more memory than ordinary
     * journaling because there are multiple connections to the same database.
     * So if a database will only be used by a single thread, or if optimizing
     * concurrency is not very important, then write-ahead logging should be disabled.
//SQLiteDatabase.java 获取最多的执行数,由系统决定
  private void setMaxConnectionPoolSizeLocked() {
        if (!mConfiguration.isInMemoryDb()
                && (mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) {
            mMaxConnectionPoolSize = SQLiteGlobal.getWALConnectionPoolSize();
        } else {
            // We don't actually need to always restrict the connection pool size to 1
            // for non-WAL databases.  There might be reasons to use connection pooling
            // with other journal modes. However, we should always keep pool size of 1 for in-memory
            // databases since every :memory: db is separate from another.
            // For now, enabling connection pooling and using WAL are the same thing in the API.
            mMaxConnectionPoolSize = 1;
        }
    }

你可能感兴趣的:(Android,Java)