Android SQLite的 select 操作分析 .
来源:互联网 发布:室内烧烤知乎 编辑:程序博客网 时间:2024/06/07 02:05
就像 Android SQLiteStatement 编译、执行 分析 中所说的,SQLite中所有SQL语句都需要先编译为stmt,然后执行。
上述文章介绍了SQLiteStatement在android层面的编译执行。然而,类SQLiteStatement只能用以执行无返回值或者只有一行一列(1X1)的sql语句,例如INSERT ALERT 等,而像SELECT这种返回结果可能多行多列的则不适用。
android对select提供了专门的执行方法rawQuery(),对其也有特殊的SQLiteQuery类,以及相关的Cursor类。这篇文章我们可以看到,其实SQLiteQuery与SQLiteStatement本质是相同的,android针对select的特殊性做了特殊的执行流程。
这里出现了两步操作,构建一个driver,通过driver执行。那么 SQLiteDirectCursorDriver 是什么呢?
可以看到,driver的一个陌生成员变量为SQLiteQuery,并且它在构造函数中并未出现,在执行driver.query时才出现并被赋值③。接着又构造了我们熟悉的cursor并将其返回④。
在④中可以看到,如果factory == null,即没有自定义的cursor工厂类(我们一般不会自定义的),会直接构造一个SQLiteCursor。具体看下SQLiteCursor类。
可以看到,SQLiteCursor维护的也是一些元信息,但其继承自 AbstractWindowedCursor,后者又继承自AbstractCursor。
通过继承,SQLiteCursor有了指向cursor window的能力,但是在构造函数中并未体现,并且driver.query时,直接将new处的cursor返回了。此时,尚未通过native实际执行过select语句。
到这里可以看到,第一次moveToPosition时,因为此时mCount为-1,fillWindow(0)。
先看第⑧中,fillWindow()
可以看到,最终仍是通过SQLiteConnection连接到native来执行。
上述文章介绍了SQLiteStatement在android层面的编译执行。然而,类SQLiteStatement只能用以执行无返回值或者只有一行一列(1X1)的sql语句,例如INSERT ALERT 等,而像SELECT这种返回结果可能多行多列的则不适用。
android对select提供了专门的执行方法rawQuery(),对其也有特殊的SQLiteQuery类,以及相关的Cursor类。这篇文章我们可以看到,其实SQLiteQuery与SQLiteStatement本质是相同的,android针对select的特殊性做了特殊的执行流程。
1、使用query的方式
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- Cursor cr;
- cr = db.rawQuery("select * from person where age=20", null);
- if (cr.moveToFirst()) {
- for (int i = 0; i < cr.getCount(); i++) {
- cr.getString();
- cr.moveToNext();
- }
- }
SQLiteDatabase db = mOpenHelper.getWritableDatabase();Cursor cr;cr = db.rawQuery("select * from person where age=20", null);if (cr.moveToFirst()) { for (int i = 0; i < cr.getCount(); i++) { cr.getString(); cr.moveToNext(); }}
2、query的操作
- //SQLiteDatabase.java
- public Cursor rawQuery(String sql, String[] selectionArgs) {
- return rawQueryWithFactory(null, sql, selectionArgs, null, null);
- }
- public Cursor rawQueryWithFactory(
- CursorFactory cursorFactory, String sql, String[] selectionArgs,
- String editTable, CancellationSignal cancellationSignal) {
- acquireReference();
- try {
- SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable,
- cancellationSignal); // ①
- return driver.query(cursorFactory != null ? cursorFactory : mCursorFactory,
- selectionArgs); // ②
- } finally {
- releaseReference();
- }
- }
//SQLiteDatabase.java public Cursor rawQuery(String sql, String[] selectionArgs) { return rawQueryWithFactory(null, sql, selectionArgs, null, null); } public Cursor rawQueryWithFactory( CursorFactory cursorFactory, String sql, String[] selectionArgs, String editTable, CancellationSignal cancellationSignal) { acquireReference(); try { SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable, cancellationSignal); // ① return driver.query(cursorFactory != null ? cursorFactory : mCursorFactory, selectionArgs); // ② } finally { releaseReference(); } }
这里出现了两步操作,构建一个driver,通过driver执行。那么 SQLiteDirectCursorDriver 是什么呢?
- // SQLiteDirectCursorDriver.java
- public final class SQLiteDirectCursorDriver implements SQLiteCursorDriver {
- private final SQLiteDatabase mDatabase;
- private final String mEditTable;
- private final String mSql;
- private final CancellationSignal mCancellationSignal;
- private SQLiteQuery mQuery;
- public SQLiteDirectCursorDriver(SQLiteDatabase db, String sql, String editTable,
- CancellationSignal cancellationSignal) {
- mDatabase = db;
- mEditTable = editTable;
- mSql = sql;
- mCancellationSignal = cancellationSignal;
- }
- public Cursor query(CursorFactory factory, String[] selectionArgs) {
- final SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, mCancellationSignal); //③
- final Cursor cursor;
- try {
- query.bindAllArgsAsStrings(selectionArgs);
- if (factory == null) {
- cursor = new SQLiteCursor(this, mEditTable, query); // ④
- } else {
- cursor = factory.newCursor(mDatabase, this, mEditTable, query);
- }
- } catch (RuntimeException ex) {
- query.close();
- throw ex;
- }
- mQuery = query;
- return cursor;
- }
// SQLiteDirectCursorDriver.javapublic final class SQLiteDirectCursorDriver implements SQLiteCursorDriver { private final SQLiteDatabase mDatabase; private final String mEditTable; private final String mSql; private final CancellationSignal mCancellationSignal; private SQLiteQuery mQuery; public SQLiteDirectCursorDriver(SQLiteDatabase db, String sql, String editTable, CancellationSignal cancellationSignal) { mDatabase = db; mEditTable = editTable; mSql = sql; mCancellationSignal = cancellationSignal; } public Cursor query(CursorFactory factory, String[] selectionArgs) { final SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, mCancellationSignal); //③ final Cursor cursor; try { query.bindAllArgsAsStrings(selectionArgs); if (factory == null) { cursor = new SQLiteCursor(this, mEditTable, query); // ④ } else { cursor = factory.newCursor(mDatabase, this, mEditTable, query); } } catch (RuntimeException ex) { query.close(); throw ex; } mQuery = query; return cursor; }
可以看到,driver的一个陌生成员变量为SQLiteQuery,并且它在构造函数中并未出现,在执行driver.query时才出现并被赋值③。接着又构造了我们熟悉的cursor并将其返回④。
首先看下SQLiteQuery:
- // SQLiteQuery.java
- public final class SQLiteQuery extends SQLiteProgram {
- private static final String TAG = "SQLiteQuery";
- private final CancellationSignal mCancellationSignal;
- SQLiteQuery(SQLiteDatabase db, String query, CancellationSignal cancellationSignal) {
- super(db, query, null, cancellationSignal);
- mCancellationSignal = cancellationSignal;
- }
// SQLiteQuery.javapublic final class SQLiteQuery extends SQLiteProgram { private static final String TAG = "SQLiteQuery"; private final CancellationSignal mCancellationSignal; SQLiteQuery(SQLiteDatabase db, String query, CancellationSignal cancellationSignal) { super(db, query, null, cancellationSignal); mCancellationSignal = cancellationSignal; }正如文章开头所说,SQLiteQuery继承自SQLiteProgram,和SQLiteStatement相同。由(Android SQLiteStatement 编译、执行 分析)可以知道,在其构造函数中,经历了sql语句的prepare过程,在某个连接池的某个connection中已经含有了相应的stmt。
在④中可以看到,如果factory == null,即没有自定义的cursor工厂类(我们一般不会自定义的),会直接构造一个SQLiteCursor。具体看下SQLiteCursor类。
- // SQLiteCursor.java
- public class SQLiteCursor extends AbstractWindowedCursor {
- static final String TAG = "SQLiteCursor";
- static final int NO_COUNT = -1;
- private final String mEditTable;
- private final String[] mColumns;
- private final SQLiteQuery mQuery;
- private final SQLiteCursorDriver mDriver;
- private int mCount = NO_COUNT;
- private int mCursorWindowCapacity;
- private Map<String, Integer> mColumnNameMap;
- private final Throwable mStackTrace;
- public SQLiteCursor(SQLiteCursorDriver driver, String editTable, SQLiteQuery query) {
- ……
- mDriver = driver;
- mEditTable = editTable;
- mColumnNameMap = null;
- mQuery = query;
- mColumns = query.getColumnNames();
- mRowIdColumnIndex = DatabaseUtils.findRowIdColumnIndex(mColumns);
- }
// SQLiteCursor.javapublic class SQLiteCursor extends AbstractWindowedCursor { static final String TAG = "SQLiteCursor"; static final int NO_COUNT = -1; private final String mEditTable; private final String[] mColumns; private final SQLiteQuery mQuery; private final SQLiteCursorDriver mDriver; private int mCount = NO_COUNT; private int mCursorWindowCapacity; private Map<String, Integer> mColumnNameMap; private final Throwable mStackTrace; public SQLiteCursor(SQLiteCursorDriver driver, String editTable, SQLiteQuery query) { …… mDriver = driver; mEditTable = editTable; mColumnNameMap = null; mQuery = query; mColumns = query.getColumnNames(); mRowIdColumnIndex = DatabaseUtils.findRowIdColumnIndex(mColumns); }
可以看到,SQLiteCursor维护的也是一些元信息,但其继承自 AbstractWindowedCursor,后者又继承自AbstractCursor。
- // AbstractWindowedCursor.java
- public abstract class AbstractWindowedCursor extends AbstractCursor {
- protected CursorWindow mWindow;
- }
- // AbstractCursor.java
- public abstract class AbstractCursor implements CrossProcessCursor {
- protected int mPos;
- ......
- }
// AbstractWindowedCursor.javapublic abstract class AbstractWindowedCursor extends AbstractCursor { protected CursorWindow mWindow;}// AbstractCursor.javapublic abstract class AbstractCursor implements CrossProcessCursor { protected int mPos; ......}在AbstractWindowedCursor中,我们看到了CursorWindow,在数据库中cursor window是很重要的概念。
- // CursorWindow.java
- public class CursorWindow extends SQLiteClosable implements Parcelable {
- public int mWindowPtr; // !!!
- private int mStartPos;
- private final String mName;
- private final CloseGuard mCloseGuard = CloseGuard.get();
- private static native int nativeCreate(String name, int cursorWindowSize);
- private static native void nativeClear(int windowPtr);
- private static native int nativeGetNumRows(int windowPtr);
- private static native double nativeGetDouble(int windowPtr, int row, int column);
- ……
- }
// CursorWindow.javapublic class CursorWindow extends SQLiteClosable implements Parcelable { public int mWindowPtr; // !!! private int mStartPos; private final String mName; private final CloseGuard mCloseGuard = CloseGuard.get(); private static native int nativeCreate(String name, int cursorWindowSize); private static native void nativeClear(int windowPtr); private static native int nativeGetNumRows(int windowPtr); private static native double nativeGetDouble(int windowPtr, int row, int column); ……}mWindowPtr 目测是指向native层sqlite相应window的指针。并且该类含有不少native方法,部分对sqlite中window的操作,应该是通过这个类实现的。
通过继承,SQLiteCursor有了指向cursor window的能力,但是在构造函数中并未体现,并且driver.query时,直接将new处的cursor返回了。此时,尚未通过native实际执行过select语句。
3、Cursor的操作
- //AbstractCursor.java
- public final boolean moveToFirst() {
- return moveToPosition(0);
- }
- public final boolean moveToNext() {
- return moveToPosition(mPos + 1);
- }
- public final boolean moveToPosition(int position) {
- final int count = getCount(); // ⑤
- if (position >= count) {
- mPos = count;
- return false;
- }
- if (position < 0) {
- mPos = -1;
- return false;
- }
- if (position == mPos) {
- return true;
- }
- boolean result = onMove(mPos, position); /// ⑨
- if (result == false) {
- mPos = -1;
- } else {
- mPos = position;
- if (mRowIdColumnIndex != -1) {
- mCurrentRowID = Long.valueOf(getLong(mRowIdColumnIndex));
- }
- }
- return result;
- }
- // SQLiteCursor.java
- @Override
- public int getCount() {
- if (mCount == NO_COUNT) {
- fillWindow(0);
- }
- return mCount;
- }
- @Override
- public boolean onMove(int oldPosition, int newPosition) {
- if (mWindow == null || newPosition < mWindow.getStartPosition() ||
- newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) {
- fillWindow(newPosition);
- }
- return true;
- }
//AbstractCursor.java public final boolean moveToFirst() { return moveToPosition(0); } public final boolean moveToNext() { return moveToPosition(mPos + 1); } public final boolean moveToPosition(int position) { final int count = getCount(); // ⑤ if (position >= count) { mPos = count; return false; } if (position < 0) { mPos = -1; return false; } if (position == mPos) { return true; } boolean result = onMove(mPos, position); /// ⑨ if (result == false) { mPos = -1; } else { mPos = position; if (mRowIdColumnIndex != -1) { mCurrentRowID = Long.valueOf(getLong(mRowIdColumnIndex)); } } return result; }// SQLiteCursor.java @Override public int getCount() { if (mCount == NO_COUNT) { fillWindow(0); } return mCount; } @Override public boolean onMove(int oldPosition, int newPosition) { if (mWindow == null || newPosition < mWindow.getStartPosition() || newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) { fillWindow(newPosition); } return true; }
到这里可以看到,第一次moveToPosition时,因为此时mCount为-1,fillWindow(0)。
- // SQLiteCursor.java
- private void fillWindow(int requiredPos) {
- clearOrCreateWindow(getDatabase().getPath()); // ⑥
- if (mCount == NO_COUNT) {
- int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos, 0); // ⑦
- mCount = mQuery.fillWindow(mWindow, startPos, requiredPos, true); // ⑧
- mCursorWindowCapacity = mWindow.getNumRows();
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "received count(*) from native_fill_window: " + mCount);
- }
- } else {
- int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos,
- mCursorWindowCapacity);
- mQuery.fillWindow(mWindow, startPos, requiredPos, false);
- }
- }
- protected void clearOrCreateWindow(String name) {
- if (mWindow == null) { // 建立CursorWindow
- mWindow = new CursorWindow(name);
- } else {
- mWindow.clear();
- }
- }
// SQLiteCursor.java private void fillWindow(int requiredPos) { clearOrCreateWindow(getDatabase().getPath()); // ⑥ if (mCount == NO_COUNT) { int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos, 0); // ⑦ mCount = mQuery.fillWindow(mWindow, startPos, requiredPos, true); // ⑧ mCursorWindowCapacity = mWindow.getNumRows(); if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "received count(*) from native_fill_window: " + mCount); } } else { int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos, mCursorWindowCapacity); mQuery.fillWindow(mWindow, startPos, requiredPos, false); } } protected void clearOrCreateWindow(String name) { if (mWindow == null) { // 建立CursorWindow mWindow = new CursorWindow(name); } else { mWindow.clear(); } }在第⑥中,new出CursorWindow,将其赋值给mWindow,此时,由SQLiteCursor掌管。如下,new CursorWindow的过程,调用了nativeCreate,并使mWindowPtr指向native层的window。
- // CursorWindow.java
- public CursorWindow(String name) {
- mStartPos = 0;
- mName = name != null && name.length() != 0 ? name : "<unnamed>";
- mWindowPtr = nativeCreate(mName, sCursorWindowSize); // !!!
- if (mWindowPtr == 0) {
- throw new CursorWindowAllocationException("Cursor window allocation of " +
- (sCursorWindowSize / 1024) + " kb failed. " + printStats());
- }
- mCloseGuard.open("close");
- recordNewWindow(Binder.getCallingPid(), mWindowPtr);
- }
// CursorWindow.java public CursorWindow(String name) { mStartPos = 0; mName = name != null && name.length() != 0 ? name : "<unnamed>"; mWindowPtr = nativeCreate(mName, sCursorWindowSize); // !!! if (mWindowPtr == 0) { throw new CursorWindowAllocationException("Cursor window allocation of " + (sCursorWindowSize / 1024) + " kb failed. " + printStats()); } mCloseGuard.open("close"); recordNewWindow(Binder.getCallingPid(), mWindowPtr); }
先看第⑧中,fillWindow()
- // SQLiteQuery.java
- int fillWindow(CursorWindow window, int startPos, int requiredPos, boolean countAllRows) {
- ....
- int numRows = getSession().executeForCursorWindow(getSql(), getBindArgs(),
- window, startPos, requiredPos, countAllRows, getConnectionFlags(),
- mCancellationSignal);
- return numRows;
- }
- // SQLiteSeesion.java
- public int executeForCursorWindow(String sql, Object[] bindArgs,
- CursorWindow window, int startPos, int requiredPos, boolean countAllRows,
- int connectionFlags, CancellationSignal cancellationSignal) {
- acquireConnection(sql, connectionFlags, cancellationSignal);
- try {
- return mConnection.executeForCursorWindow(sql, bindArgs,
- window, startPos, requiredPos, countAllRows,
- cancellationSignal);
- } finally {
- releaseConnection();
- }
- }
- // SQLiteConnection.java
- public int executeForCursorWindow(String sql, Object[] bindArgs,
- CursorWindow window, int startPos, int requiredPos, boolean countAllRows,
- CancellationSignal cancellationSignal) {
- final PreparedStatement statement = acquirePreparedStatement(sql);
- final long result = nativeExecuteForCursorWindow( // !!!
- mConnectionPtr, statement.mStatementPtr, window.mWindowPtr,
- startPos, requiredPos, countAllRows);
- actualPos = (int)(result >> 32);
- countedRows = (int)result;
- filledRows = window.getNumRows();
- window.setStartPosition(actualPos);
- return countedRows;
- .....
- }
// SQLiteQuery.java int fillWindow(CursorWindow window, int startPos, int requiredPos, boolean countAllRows) { .... int numRows = getSession().executeForCursorWindow(getSql(), getBindArgs(), window, startPos, requiredPos, countAllRows, getConnectionFlags(), mCancellationSignal); return numRows; }// SQLiteSeesion.java public int executeForCursorWindow(String sql, Object[] bindArgs, CursorWindow window, int startPos, int requiredPos, boolean countAllRows, int connectionFlags, CancellationSignal cancellationSignal) { acquireConnection(sql, connectionFlags, cancellationSignal); try { return mConnection.executeForCursorWindow(sql, bindArgs, window, startPos, requiredPos, countAllRows, cancellationSignal); } finally { releaseConnection(); } }// SQLiteConnection.java public int executeForCursorWindow(String sql, Object[] bindArgs, CursorWindow window, int startPos, int requiredPos, boolean countAllRows, CancellationSignal cancellationSignal) { final PreparedStatement statement = acquirePreparedStatement(sql); final long result = nativeExecuteForCursorWindow( // !!! mConnectionPtr, statement.mStatementPtr, window.mWindowPtr, startPos, requiredPos, countAllRows); actualPos = (int)(result >> 32); countedRows = (int)result; filledRows = window.getNumRows(); window.setStartPosition(actualPos); return countedRows; ..... }
可以看到,最终仍是通过SQLiteConnection连接到native来执行。
剩下的getString就比较简单了,一直会调用到到mWindow的getString
- public String getString(int row, int column) {
- acquireReference();
- try {
- return nativeGetString(mWindowPtr, row - mStartPos, column);
- } finally {
- releaseReference();
- }
- }
public String getString(int row, int column) { acquireReference(); try { return nativeGetString(mWindowPtr, row - mStartPos, column); } finally { releaseReference(); } }
最后看下第⑦,即window fill 的控制。
这里有涉及fill策略,一般无需考虑。如果结果集大于window怎么办?如果所需某个元素不在window中怎么办?尚未详细分析了,贴下代码。
若是第一次fill,required row 为0,即从第一条记录开始fill满window。
window将会包含所需的row及其周围的一些row。例如,想要结果集的第120个元素,window大小为90,则将结果集第90-180的元素填充至window,120之前30个,之后60个。 如果window中没有,将其放置在window的第10个位置。
- // DatabaseUtils.java
- public static int cursorPickFillWindowStartPosition(
- int cursorPosition, int cursorWindowCapacity) {
- return Math.max(cursorPosition - cursorWindowCapacity / 3, 0);
- }
// DatabaseUtils.java public static int cursorPickFillWindowStartPosition( int cursorPosition, int cursorWindowCapacity) { return Math.max(cursorPosition - cursorWindowCapacity / 3, 0); }
- static jlong nativeExecuteForCursorWindow(JNIEnv* env, jclass clazz,
- jint connectionPtr, jint statementPtr, jint windowPtr,
- jint startPos, jint requiredPos, jboolean countAllRows) {
- ......
- int retryCount = 0;
- int totalRows = 0;
- int addedRows = 0;
- bool windowFull = false;
- bool gotException = false;
- while (!gotException && (!windowFull || countAllRows)) {
- int err = sqlite3_step(statement);
- if (err == SQLITE_ROW) {
- LOG_WINDOW("Stepped statement %p to row %d", statement, totalRows);
- retryCount = 0;
- totalRows += 1;
- // Skip the row if the window is full or we haven't reached the start position yet.
- if (startPos >= totalRows || windowFull) {
- continue;
- }
- CopyRowResult cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);
- if (cpr == CPR_FULL && addedRows && startPos + addedRows < requiredPos) {
- // We filled the window before we got to the one row that we really wanted.
- // Clear the window and start filling it again from here.
- // TODO: Would be nicer if we could progressively replace earlier rows.
- window->clear();
- window->setNumColumns(numColumns);
- startPos += addedRows;
- addedRows = 0;
- cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);
- }
- if (cpr == CPR_OK) {
- addedRows += 1;
- } else if (cpr == CPR_FULL) {
- windowFull = true;
- } else {
- gotException = true;
- }
- } else if (err == SQLITE_DONE) {
- // All rows processed, bail
- LOG_WINDOW("Processed all rows");
- break;
- } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) {
- // The table is locked, retry
- LOG_WINDOW("Database locked, retrying");
- if (retryCount > 50) {
- ALOGE("Bailing on database busy retry");
- throw_sqlite3_exception(env, connection->db, "retrycount exceeded");
- gotException = true;
- } else {
- // Sleep to give the thread holding the lock a chance to finish
- usleep(1000);
- retryCount++;
- }
- } else {
- throw_sqlite3_exception(env, connection->db);
- gotException = true;
- }
- }
- LOG_WINDOW("Resetting statement %p after fetching %d rows and adding %d rows"
- "to the window in %d bytes",
- statement, totalRows, addedRows, window->size() - window->freeSpace());
- sqlite3_reset(statement);
- // Report the total number of rows on request.
- if (startPos > totalRows) {
- ALOGE("startPos %d > actual rows %d", startPos, totalRows);
- }
- jlong result = jlong(startPos) << 32 | jlong(totalRows);
- return result;
- }
static jlong nativeExecuteForCursorWindow(JNIEnv* env, jclass clazz, jint connectionPtr, jint statementPtr, jint windowPtr, jint startPos, jint requiredPos, jboolean countAllRows) { ...... int retryCount = 0; int totalRows = 0; int addedRows = 0; bool windowFull = false; bool gotException = false; while (!gotException && (!windowFull || countAllRows)) { int err = sqlite3_step(statement); if (err == SQLITE_ROW) { LOG_WINDOW("Stepped statement %p to row %d", statement, totalRows); retryCount = 0; totalRows += 1; // Skip the row if the window is full or we haven't reached the start position yet. if (startPos >= totalRows || windowFull) { continue; } CopyRowResult cpr = copyRow(env, window, statement, numColumns, startPos, addedRows); if (cpr == CPR_FULL && addedRows && startPos + addedRows < requiredPos) { // We filled the window before we got to the one row that we really wanted. // Clear the window and start filling it again from here. // TODO: Would be nicer if we could progressively replace earlier rows. window->clear(); window->setNumColumns(numColumns); startPos += addedRows; addedRows = 0; cpr = copyRow(env, window, statement, numColumns, startPos, addedRows); } if (cpr == CPR_OK) { addedRows += 1; } else if (cpr == CPR_FULL) { windowFull = true; } else { gotException = true; } } else if (err == SQLITE_DONE) { // All rows processed, bail LOG_WINDOW("Processed all rows"); break; } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) { // The table is locked, retry LOG_WINDOW("Database locked, retrying"); if (retryCount > 50) { ALOGE("Bailing on database busy retry"); throw_sqlite3_exception(env, connection->db, "retrycount exceeded"); gotException = true; } else { // Sleep to give the thread holding the lock a chance to finish usleep(1000); retryCount++; } } else { throw_sqlite3_exception(env, connection->db); gotException = true; } } LOG_WINDOW("Resetting statement %p after fetching %d rows and adding %d rows" "to the window in %d bytes", statement, totalRows, addedRows, window->size() - window->freeSpace()); sqlite3_reset(statement); // Report the total number of rows on request. if (startPos > totalRows) { ALOGE("startPos %d > actual rows %d", startPos, totalRows); } jlong result = jlong(startPos) << 32 | jlong(totalRows); return result;}
4、总结
① query的执行同普通sql语句相同,都需经过sql语句的编译及执行。
② 编译后为SQLiteQuery,执行后返回SQLiteCursor,SQLiteCursor的mWindow指向native层的cursor window。
③ 通过SQLiteCursor对返回结果进行控制。
④ 执行的过程,是构建SQLiteCursor的过程,并未将结果集写入相应window。
⑤ 结果集写入window,发生在第一次类似cursor.moveToFirst()操作中。这是android中处处体现的惰性策略。
⑥ sqlite本身对结果集与window的关系做了优化,android在此基础上再次优化,以应对结果集过大、跳跃式读取结果等问题。尚未分析。
0 0
- Android SQLite的 select 操作分析
- Android SQLite的 select 操作分析 .
- Android SQLite 打开、操作分析
- android Sqlite数据库的操作
- android sqlite数据库的操作
- android的sqlite数据库操作
- android中SQLite的操作
- Android的SQLite基本操作
- Android SQLite的基本操作
- Android 的 Sqlite基本操作
- Android 的 Sqlite基本操作
- android数据库sqlite 不支持select top 10的写法
- Android的SQLite数据库的操作
- SQLite进阶:Android上的SQLite常用操作
- 【android】SQLite数据库的创建和操作、SQLite数据库可视化
- android中SQLite数据库的操作
- android中SQLite数据库的操作
- Android SQLite的一些操作细节
- Construct Binary Tree from Inorder and Postorder Traversal
- 解决tomcat6.0启动一闪而过的方法
- 字符串循环移位
- POCO 的 Zip 类对中文文件名支持不正确的解决方法
- 三元组实现矩阵转置.cpp
- Android SQLite的 select 操作分析 .
- sql之表连接
- uvaLA 3695
- Linux运维第2天:Linux文件系统详解
- 垃圾收集器与内存分配策略
- 软考之路(八)--- 大总结
- 解决nginx 504 Gateway Time-out的一些方法
- 空中上网,且飞且互联
- hadoop中wordcount遍历子目录的情况