【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.javapublic 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;        }    }

更多相关文章

  1. android 调用系统计算器 实例
  2. [置顶] Android(安卓)SQlite使用实践Demo
  3. Cocos Creator与Android(安卓)接口互调
  4. Android实现授权访问网页的方法
  5. Android(安卓)startActivityForResult的使用
  6. 也说Activity任务栈
  7. android空指针的bug
  8. XXXX软件Android平台移植Porting说明书
  9. Android(安卓)数据库框架OrmLite的使用(一)

随机推荐

  1. android中log知识总结
  2. android notes(1)
  3. android 自定义组件圆形边框
  4. Ubuntu 将adb加入环境变量
  5. Android控件常用属性
  6. android 去掉应用程序自带的黑色的头部横
  7. Android架构图
  8. Android中 Rect类
  9. Android:SeekBar和RatingBar控件
  10. Android 安装环境搭建