SQLite数据库在Android系统中的应用--SDK封装

SQLite数据库在Android系统java层的核心类包括SQLiteDatabase, SQLiteConnection, SQLiteConnectionPool, SQLiteSession以及查询操作的SQLiteCursorCursorWindow, 关于查询操作的两个类请参考SQLite数据库的cursor在Android系统Java层及JNI层的实现机制一文的说明.

SQLiteDatabase是提供给开发人员使用的类, 包括了对数据库连接的操作命令, SQL语句的执行命令,以及一些其他的数据库管理命令等.

SQLiteConnection代表数据库连接. 该类在java层和JNI层都有实现.SQLite数据库文件每次被打开时, 在底层都返回一个sqlite3对象, SQLiteConnection类对象可以认为是sqlite3对象的封装.

SQLiteConnectionPool代表数据库连接池, 即用来管理SQLiteConnection对象. 一个SQLiteConnection对象要么存在于该类对象的数据库连接池中,要么被一个SQLiteSession对象所持有.根据数据库连接池的配置, SQLiteConnectionPool可以根据需要动态的创建SQLiteConnection对象.

SQLiteSession代表数据库会话.SQLiteDatabase对象在执行数据库命令时,是通过数据库会话对象来执行的, 其管理数据库连接的生命周期.数据库会话对象管理的数据库连接是从数据库连接池中分配的, 命令执行完毕后, 将数据库连接归还数据库连接池.

这几个核心类对象间有如下的关系:

  • 一个SQLiteDatabase对象中存在一个SQLiteConnectionPool对象成员变量. 是一对一的关系.
  • 一个SQLiteSession对象中存在一个SQLiteConnection对象成员变量, 是一对一的关系.因为该数据库连接对象是从数据库连接池中分配的, 所以SQLiteSession对象中还包含了一个SQLiteConnectionPool对象成员变量.
  • 一个SQLiteConnectionPool对象可以包含多个SQLiteConnection对象:
// Strong references to all available connections.
private final ArrayList<SQLiteConnection> mAvailableNonPrimaryConnections =
       new ArrayList<SQLiteConnection>();
private SQLiteConnection mAvailablePrimaryConnection;

SQLiteConnectionPool对象中将SQLiteConnectionPool对象区分为两种:mAvailableNonPrimaryConnectionsmAvailablePrimaryConnection, 这可以在java层更方便的控制串行访问. 但是这两种java层的数据库连接对象在SQLite底层对应的sqlite3对象是没有任何区别的.

  • 一个SQLiteDatabase对象中存在一个ThreadLocal方式保存的SQLiteSession对象成员变量:
  private final ThreadLocal<SQLiteSession> mThreadSession = new ThreadLocal<SQLiteSession>() {
    @Override
    protected SQLiteSession initialValue() {
        return createSession();
    }
};

所以, SQLiteDatabase对象是可以在多线程中使用的, 这时,一个SQLiteDatabase对象可能有多个SQLiteSession对象.而多线程环境中的SQLiteSession对象都是从SQLiteDatabase对象对应的SQLiteConnectionPool对象中分配数据库连接对象, 所以要求SQLiteConnectionPool对象是线程安全的.

下面通过几个典型的数据库操作,从源码角度了解这几个核心类间的交互.

open操作
首先看打开数据库连接的操作,SQLiteDatabase类共过静态方法open打开一个数据库连接(如果数据库文件不存在,需要创建该文件),并返回该类的一个对象.主要执行函数为(SQLiteDatabase.java):

public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags,
        DatabaseErrorHandler errorHandler) {
    SQLiteDatabase db = new SQLiteDatabase(path, flags, factory, errorHandler);
    db.open();
    return db;
}

private void openInner() {
    synchronized (mLock) {
        assert mConnectionPoolLocked == null;
        mConnectionPoolLocked = SQLiteConnectionPool.open(mConfigurationLocked);
        mCloseGuardLocked.open("close");
    }

    synchronized (sActiveDatabases) {
        sActiveDatabases.put(this, null);
    }
}

进入SQLiteConnectionPool调用其open函数(SQLiteConnectionPool.java).

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;
}

// Might throw
private void open() {
    // Open the primary connection.
    // This might throw if the database is corrupt.
    mAvailablePrimaryConnection = openConnectionLocked(mConfiguration,
            true /*primaryConnection*/); // might throw

    // Mark the pool as being open for business.
    mIsOpen = true;
    mCloseGuard.open("close");
}

// Might throw.
private SQLiteConnection openConnectionLocked(SQLiteDatabaseConfiguration configuration,
        boolean primaryConnection) {
    final int connectionId = mNextConnectionId++;
    return SQLiteConnection.open(this, configuration,
            connectionId, primaryConnection); // might throw
}

接着进入SQLiteConnection类,调用其open函数(SQLiteConnection.java).

// Might throw.
private SQLiteConnection openConnectionLocked(SQLiteDatabaseConfiguration configuration,
        boolean primaryConnection) {
    final int connectionId = mNextConnectionId++;
    return SQLiteConnection.open(this, configuration,
            connectionId, primaryConnection); // might throw
}

// Called by SQLiteConnectionPool only.
static SQLiteConnection open(SQLiteConnectionPool pool,
        SQLiteDatabaseConfiguration configuration,
        int connectionId, boolean primaryConnection) {
    SQLiteConnection connection = new SQLiteConnection(pool, configuration,
            connectionId, primaryConnection);
    try {
        connection.open();
        return connection;
    } catch (SQLiteException ex) {
        connection.dispose(false);
        throw ex;
    }
}

private void open() {
    mConnectionPtr = nativeOpen(mConfiguration.path, mConfiguration.openFlags,
            mConfiguration.label,
            SQLiteDebug.DEBUG_SQL_STATEMENTS, SQLiteDebug.DEBUG_SQL_TIME,
            mConfiguration.lookasideSlotSize, mConfiguration.lookasideSlotCount);
    setPageSize();
    setForeignKeyModeFromConfiguration();
    setWalModeFromConfiguration();
    setJournalSizeLimit();
    setAutoCheckpointInterval();
    setLocaleFromConfiguration();

    // Register custom functions.
    final int functionCount = mConfiguration.customFunctions.size();
    for (int i = 0; i < functionCount; i++) {
        SQLiteCustomFunction function = mConfiguration.customFunctions.get(i);
        nativeRegisterCustomFunction(mConnectionPtr, function);
    }
}

所以会调用JNI层的SQLiteConnection对象打开SQLite数据库底层的连接,并将其指针赋值给java层的mConnectionPtr变量.然后设置数据库连接的一些属性,比如page大小,外键约束,是否是WAL模式等等.

所以, SQLiteDatabase的open调用将打开底层SQLite数据库连接,并建立了SQLiteDatabase, SQLiteConnectionPool和SQLiteConnection三者间的联系.

execSQL操作
SQLiteDatabase类对象调用execSQL函数执行SQL语句的主要步骤如下(SQLiteDatabase.java).

private int executeSql(String sql, Object[] bindArgs) throws SQLException {
    acquireReference();
    try {
        if (DatabaseUtils.getSqlStatementType(sql) == DatabaseUtils.STATEMENT_ATTACH) {
            boolean disableWal = false;
            synchronized (mLock) {
                if (!mHasAttachedDbsLocked) {
                    mHasAttachedDbsLocked = true;
                    disableWal = true;
                }
            }
            if (disableWal) {
                disableWriteAheadLogging();
            }
        }

        SQLiteStatement statement = new SQLiteStatement(this, sql, bindArgs);
        try {
            return statement.executeUpdateDelete();
        } finally {
            statement.close();
        }
    } finally {
        releaseReference();
    }
}

SQLiteStatement也是一个核心类,但是该类是一个数据结构类,在SQL语句执行命令中使用.该类的构造函数中将执行prepare,对SQL语句进行编译.其executeUpdateDelete为(SQLiteStatement.java):

public int executeUpdateDelete() {
    acquireReference();
    try {
        return getSession().executeForChangedRowCount(
                getSql(), getBindArgs(), getConnectionFlags(), null);
    } catch (SQLiteDatabaseCorruptException ex) {
        onCorruption();
        throw ex;
    } finally {
        releaseReference();
    }
}

protected final SQLiteSession getSession() {
    return mDatabase.getThreadSession();
}

函数getSession返回SQLiteDatabase对象在当前线程绑定的SQLiteSession对象(SQLiteSession.java).

public int executeForChangedRowCount(String sql, Object[] bindArgs, int connectionFlags,
        CancellationSignal cancellationSignal) {
    if (sql == null) {
        throw new IllegalArgumentException("sql must not be null.");
    }

    if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
        return 0;
    }

    acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
    try {
        return mConnection.executeForChangedRowCount(sql, bindArgs,
                cancellationSignal); // might throw
    } finally {
        releaseConnection(); // might throw
    }
}

private void acquireConnection(String sql, int connectionFlags,
        CancellationSignal cancellationSignal) {
    if (mConnection == null) {
        assert mConnectionUseCount == 0;
        mConnection = mConnectionPool.acquireConnection(sql, connectionFlags,
                cancellationSignal); // might throw
        mConnectionFlags = connectionFlags;
    }
    mConnectionUseCount += 1;
}

private void releaseConnection() {
    assert mConnection != null;
    assert mConnectionUseCount > 0;
    if (--mConnectionUseCount == 0) {
        try {
            mConnectionPool.releaseConnection(mConnection); // might throw
        } finally {
            mConnection = null;
        }
    }
}

可以看到, acquireConnection函数将从SQLiteConnectionPool对象中获得一个数据库连接对象.然后在该数据库连接对象上调用executeForChangedRowCount. 该函数将调用底层的SQLite数据库.SQL命令执行完毕后, SQLiteSession对象调用releaseConnection函数,将该数据库连接对象归还给数据库连接池.

因此, 在执行SQL操作时, 将获得当前线程绑定的SQLiteSession对象,然后进行SQL编译,从SQLiteConnectionPool中获得一个SQLiteConnection对象, 最后在该数据库连接对象上执行相应的操作.

尽管SQLiteDatabase对象可以在多线程场景中使用, 但是有两点需要注意:

  • 请检查是否真的需要在多线程场景中使用SQLiteDatabase对象, 因为这可能导致代码可读性和可维护性变差.
  • 在多线程场景中使用SQLiteDatabase对象时,虽然不会有安全问题,但是可能存在阻塞. 阻塞的场景有:在java层, 可能无法从SQLiteConnectionPool对象中获得数据库连接对象,因为SQLiteConnectionPool对象中分配的数据库连接对象已经达到配置上限.在SQLite数据库底层, 由于多线程连接中的读写属性导致阻塞.

开发人员可以直接使用SQLiteDatabase提供的接口操作数据库,也可以使用SQLiteOpenHelper封装类操作数据库.该封装类依然会使用上面提到的几个核心类来操作数据库.关于SQLiteOpenHelper类有几点需要注意的是:

  • 该类的构造函数并不会打开数据库连接. 在调用其getReadableDatabase或者getWritableDatabase时才会打开数据库连接.
  • 该类在打开数据库连接时, 提供了几个回调函数:onConfigure, onCreate,onDowngrade, onUpgradeonOpen.相对于直接使用SQLiteDatabase对象而言, 这只是改变了使用方式, 其实并没有任何新的功能. 也可以使用SQLiteDatabase对象直接调用相应的接口来实现这些回调的功能, 比如可以直接调用SQLiteDatabase对象的getVersionsetVersion进行升级或者降级的处理逻辑.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值