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.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; } }
更多相关文章
- android 调用系统计算器 实例
- [置顶] Android(安卓)SQlite使用实践Demo
- Cocos Creator与Android(安卓)接口互调
- Android实现授权访问网页的方法
- Android(安卓)startActivityForResult的使用
- 也说Activity任务栈
- android空指针的bug
- XXXX软件Android平台移植Porting说明书
- Android(安卓)数据库框架OrmLite的使用(一)