Android SQLite的 select 操作分析 .

就像 Android SQLiteStatement 编译、执行 分析 中所说的,SQLite中所有SQL语句都需要先编译为stmt,然后执行。
上述文章介绍了SQLiteStatement在android层面的编译执行。然而,类SQLiteStatement只能用以执行无返回值或者只有一行一列(1X1)的sql语句,例如INSERT ALERT 等,而像SELECT这种返回结果可能多行多列的则不适用。
android对select提供了专门的执行方法rawQuery(),对其也有特殊的SQLiteQuery类,以及相关的Cursor类。这篇文章我们可以看到,其实SQLiteQuery与SQLiteStatement本质是相同的,android针对select的特殊性做了特殊的执行流程。

1、使用query的方式

  1. SQLiteDatabase db = mOpenHelper.getWritableDatabase();  
  2. Cursor cr;  
  3. cr = db.rawQuery("select * from person where age=20"null);  
  4. if (cr.moveToFirst()) {  
  5.     for (int i = 0; i < cr.getCount(); i++) {  
  6.         cr.getString();  
  7.         cr.moveToNext();  
  8.     }  
  9. }  
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的操作

  1. //SQLiteDatabase.java   
  2.     public Cursor rawQuery(String sql, String[] selectionArgs) {  
  3.         return rawQueryWithFactory(null, sql, selectionArgs, nullnull);  
  4.     }  
  5.     public Cursor rawQueryWithFactory(  
  6.             CursorFactory cursorFactory, String sql, String[] selectionArgs,  
  7.             String editTable, CancellationSignal cancellationSignal) {  
  8.         acquireReference();  
  9.         try {  
  10.             SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable,  
  11.                     cancellationSignal);  // ①   
  12.             return driver.query(cursorFactory != null ? cursorFactory : mCursorFactory,  
  13.                     selectionArgs);       // ②   
  14.         } finally {  
  15.             releaseReference();  
  16.         }  
  17.     }  
//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 是什么呢?

  1. // SQLiteDirectCursorDriver.java   
  2. public final class SQLiteDirectCursorDriver implements SQLiteCursorDriver {  
  3.     private final SQLiteDatabase mDatabase;  
  4.     private final String mEditTable;   
  5.     private final String mSql;  
  6.     private final CancellationSignal mCancellationSignal;  
  7.     private SQLiteQuery mQuery;  
  8.   
  9.     public SQLiteDirectCursorDriver(SQLiteDatabase db, String sql, String editTable,  
  10.             CancellationSignal cancellationSignal) {   
  11.         mDatabase = db;  
  12.         mEditTable = editTable;  
  13.         mSql = sql;  
  14.         mCancellationSignal = cancellationSignal;  
  15.     }  
  16.   
  17.     public Cursor query(CursorFactory factory, String[] selectionArgs) {  
  18.         final SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, mCancellationSignal); //③   
  19.         final Cursor cursor;  
  20.         try {  
  21.             query.bindAllArgsAsStrings(selectionArgs);  
  22.             if (factory == null) {   
  23.                 cursor = new SQLiteCursor(this, mEditTable, query); // ④   
  24.             } else {  
  25.                 cursor = factory.newCursor(mDatabase, this, mEditTable, query);  
  26.             }  
  27.         } catch (RuntimeException ex) {  
  28.             query.close();  
  29.             throw ex;  
  30.         }  
  31.   
  32.         mQuery = query;    
  33.         return cursor;  
  34.     }  
// 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;
    }

可以看到,driver的一个陌生成员变量为 SQLiteQuery,并且它在构造函数中并未出现,在执行driver.query时才出现并被赋值③。接着又构造了我们熟悉的cursor并将其返回④。

首先看下SQLiteQuery:

  1. // SQLiteQuery.java   
  2. public final class SQLiteQuery extends SQLiteProgram {  
  3.     private static final String TAG = "SQLiteQuery";  
  4.     private final CancellationSignal mCancellationSignal;  
  5.     SQLiteQuery(SQLiteDatabase db, String query, CancellationSignal cancellationSignal) {  
  6.         super(db, query, null, cancellationSignal);  
  7.         mCancellationSignal = cancellationSignal;  
  8.     }  
// 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继承自SQLiteProgram,和SQLiteStatement相同。由( Android SQLiteStatement 编译、执行 分析)可以知道,在其构造函数中,经历了sql语句的prepare过程,在某个连接池的某个connection中已经含有了相应的stmt。
在④中可以看到,如果factory == null,即没有自定义的cursor工厂类(我们一般不会自定义的),会直接构造一个SQLiteCursor。具体看下 SQLiteCursor类。
  1. // SQLiteCursor.java   
  2. public class SQLiteCursor extends AbstractWindowedCursor {  
  3.     static final String TAG = "SQLiteCursor";  
  4.     static final int NO_COUNT = -1;  
  5.     private final String mEditTable;  
  6.     private final String[] mColumns;  
  7.     private final SQLiteQuery mQuery;  
  8.     private final SQLiteCursorDriver mDriver;  
  9.     private int mCount = NO_COUNT;  
  10.     private int mCursorWindowCapacity;  
  11.     private Map<String, Integer> mColumnNameMap;  
  12.     private final Throwable mStackTrace;  
  13.   
  14.     public SQLiteCursor(SQLiteCursorDriver driver, String editTable, SQLiteQuery query) {  
  15.         ……  
  16.         mDriver = driver;  
  17.         mEditTable = editTable;  
  18.         mColumnNameMap = null;  
  19.         mQuery = query;  
  20.         mColumns = query.getColumnNames();  
  21.         mRowIdColumnIndex = DatabaseUtils.findRowIdColumnIndex(mColumns);  
  22.     }  
// 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维护的也是一些元信息,但其继承自 AbstractWindowedCursor,后者又继承自AbstractCursor。

  1. // AbstractWindowedCursor.java   
  2. public abstract class AbstractWindowedCursor extends AbstractCursor {  
  3.     protected CursorWindow mWindow;  
  4. }  
  5. // AbstractCursor.java   
  6. public abstract class AbstractCursor implements CrossProcessCursor {  
  7.     protected int mPos;  
  8.     ......  
  9. }  
// AbstractWindowedCursor.java
public abstract class AbstractWindowedCursor extends AbstractCursor {
    protected CursorWindow mWindow;
}
// AbstractCursor.java
public abstract class AbstractCursor implements CrossProcessCursor {
    protected int mPos;
    ......
}
在AbstractWindowedCursor中,我们看到了 CursorWindow,在数据库中cursor window是很重要的概念。

  1. // CursorWindow.java   
  2. public class CursorWindow extends SQLiteClosable implements Parcelable {  
  3.     public int mWindowPtr;  // !!!   
  4.     private int mStartPos;  
  5.     private final String mName;  
  6.     private final CloseGuard mCloseGuard = CloseGuard.get();  
  7.   
  8.     private static native int nativeCreate(String name, int cursorWindowSize);  
  9.     private static native void nativeClear(int windowPtr);  
  10.     private static native int nativeGetNumRows(int windowPtr);  
  11.     private static native double nativeGetDouble(int windowPtr, int row, int column);  
  12.     ……  
  13. }  
// 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);
    ……
}
mWindowPtr 目测是指向native层sqlite相应window的指针。并且该类含有不少native方法,部分对sqlite中window的操作,应该是通过这个类实现的。
通过继承,SQLiteCursor有了指向cursor window的能力,但是在构造函数中并未体现,并且driver.query时,直接将new处的cursor返回了。此时,尚未通过native实际执行过select语句。

3、Cursor的操作

  1. //AbstractCursor.java   
  2.   
  3.     public final boolean moveToFirst() {  
  4.         return moveToPosition(0);  
  5.     }  
  6.     public final boolean moveToNext() {  
  7.         return moveToPosition(mPos + 1);  
  8.     }  
  9.   
  10.     public final boolean moveToPosition(int position) {  
  11.         final int count = getCount(); // ⑤   
  12.         if (position >= count) {  
  13.             mPos = count;  
  14.             return false;  
  15.         }  
  16.         if (position < 0) {  
  17.             mPos = -1;  
  18.             return false;  
  19.         }  
  20.         if (position == mPos) {  
  21.             return true;  
  22.         }  
  23.         boolean result = onMove(mPos, position); /// ⑨   
  24.         if (result == false) {  
  25.             mPos = -1;  
  26.         } else {  
  27.             mPos = position;  
  28.             if (mRowIdColumnIndex != -1) {  
  29.                 mCurrentRowID = Long.valueOf(getLong(mRowIdColumnIndex));  
  30.             }  
  31.         }  
  32.   
  33.         return result;  
  34.     }  
  35.   
  36. // SQLiteCursor.java   
  37.     @Override  
  38.     public int getCount() {  
  39.         if (mCount == NO_COUNT) {  
  40.             fillWindow(0);   
  41.         }  
  42.         return mCount;  
  43.     }  
  44.   
  45.     @Override  
  46.     public boolean onMove(int oldPosition, int newPosition) {  
  47.         if (mWindow == null || newPosition < mWindow.getStartPosition() ||  
  48.                 newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) {  
  49.             fillWindow(newPosition);  
  50.         }  
  51.         return true;  
  52.     }  
//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)。
  1. // SQLiteCursor.java   
  2.     private void fillWindow(int requiredPos) {  
  3.         clearOrCreateWindow(getDatabase().getPath());  // ⑥   
  4.   
  5.         if (mCount == NO_COUNT) {  
  6.             int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos, 0); // ⑦   
  7.             mCount = mQuery.fillWindow(mWindow, startPos, requiredPos, true); // ⑧   
  8.             mCursorWindowCapacity = mWindow.getNumRows();  
  9.             if (Log.isLoggable(TAG, Log.DEBUG)) {  
  10.                 Log.d(TAG, "received count(*) from native_fill_window: " + mCount);  
  11.             }  
  12.         } else {  
  13.             int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos,  
  14.                     mCursorWindowCapacity);    
  15.             mQuery.fillWindow(mWindow, startPos, requiredPos, false);  
  16.         }  
  17.     }  
  18.     protected void clearOrCreateWindow(String name) {  
  19.         if (mWindow == null) { // 建立CursorWindow   
  20.             mWindow = new CursorWindow(name);  
  21.         } else {  
  22.             mWindow.clear();  
  23.         }  
  24.     }  
// 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。

  1. // CursorWindow.java   
  2.     public CursorWindow(String name) {  
  3.         mStartPos = 0;  
  4.         mName = name != null && name.length() != 0 ? name : "<unnamed>";  
  5.         mWindowPtr = nativeCreate(mName, sCursorWindowSize); // !!!   
  6.         if (mWindowPtr == 0) {  
  7.             throw new CursorWindowAllocationException("Cursor window allocation of " +  
  8.                     (sCursorWindowSize / 1024) + " kb failed. " + printStats());  
  9.         }  
  10.         mCloseGuard.open("close");  
  11.         recordNewWindow(Binder.getCallingPid(), mWindowPtr);  
  12.     }  
// 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()

  1. // SQLiteQuery.java   
  2.     int fillWindow(CursorWindow window, int startPos, int requiredPos, boolean countAllRows) {  
  3.          ....  
  4.          int numRows = getSession().executeForCursorWindow(getSql(), getBindArgs(),  
  5.                         window, startPos, requiredPos, countAllRows, getConnectionFlags(),  
  6.                         mCancellationSignal);  
  7.          return numRows;  
  8.      }  
  9. // SQLiteSeesion.java   
  10.     public int executeForCursorWindow(String sql, Object[] bindArgs,  
  11.             CursorWindow window, int startPos, int requiredPos, boolean countAllRows,  
  12.             int connectionFlags, CancellationSignal cancellationSignal) {  
  13.         acquireConnection(sql, connectionFlags, cancellationSignal);   
  14.         try {  
  15.             return mConnection.executeForCursorWindow(sql, bindArgs,  
  16.                     window, startPos, requiredPos, countAllRows,  
  17.                     cancellationSignal);   
  18.         } finally {  
  19.             releaseConnection();  
  20.         }  
  21.     }  
  22. // SQLiteConnection.java   
  23.     public int executeForCursorWindow(String sql, Object[] bindArgs,  
  24.         CursorWindow window, int startPos, int requiredPos, boolean countAllRows,  
  25.         CancellationSignal cancellationSignal) {  
  26.         final PreparedStatement statement = acquirePreparedStatement(sql);  
  27.         final long result = nativeExecuteForCursorWindow(   // !!!   
  28.                 mConnectionPtr, statement.mStatementPtr, window.mWindowPtr,  
  29.                 startPos, requiredPos, countAllRows);  
  30.         actualPos = (int)(result >> 32);  
  31.         countedRows = (int)result;  
  32.         filledRows = window.getNumRows();  
  33.         window.setStartPosition(actualPos);  
  34.         return countedRows;  
  35.         .....  
  36.     }  
// 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

  1. public String getString(int row, int column) {  
  2.     acquireReference();  
  3.     try {  
  4.         return nativeGetString(mWindowPtr, row - mStartPos, column);  
  5.     } finally {  
  6.         releaseReference();  
  7.     }  
  8. }  
    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个位置。

  1. // DatabaseUtils.java   
  2.     public static int cursorPickFillWindowStartPosition(  
  3.             int cursorPosition, int cursorWindowCapacity) {  
  4.         return Math.max(cursorPosition - cursorWindowCapacity / 30);  
  5.     }  
// DatabaseUtils.java
    public static int cursorPickFillWindowStartPosition(
            int cursorPosition, int cursorWindowCapacity) {
        return Math.max(cursorPosition - cursorWindowCapacity / 3, 0);
    }

  1. static jlong nativeExecuteForCursorWindow(JNIEnv* env, jclass clazz,  
  2.         jint connectionPtr, jint statementPtr, jint windowPtr,  
  3.         jint startPos, jint requiredPos, jboolean countAllRows) {  
  4.     ......  
  5.   
  6.     int retryCount = 0;  
  7.     int totalRows = 0;  
  8.     int addedRows = 0;  
  9.     bool windowFull = false;  
  10.     bool gotException = false;  
  11.     while (!gotException && (!windowFull || countAllRows)) {  
  12.         int err = sqlite3_step(statement);  
  13.         if (err == SQLITE_ROW) {  
  14.             LOG_WINDOW("Stepped statement %p to row %d", statement, totalRows);  
  15.             retryCount = 0;  
  16.             totalRows += 1;  
  17.   
  18.             // Skip the row if the window is full or we haven't reached the start position yet.   
  19.             if (startPos >= totalRows || windowFull) {  
  20.                 continue;  
  21.             }  
  22.   
  23.             CopyRowResult cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);  
  24.             if (cpr == CPR_FULL && addedRows && startPos + addedRows < requiredPos) {  
  25.                 // We filled the window before we got to the one row that we really wanted.   
  26.                 // Clear the window and start filling it again from here.   
  27.                 // TODO: Would be nicer if we could progressively replace earlier rows.   
  28.                 window->clear();  
  29.                 window->setNumColumns(numColumns);  
  30.                 startPos += addedRows;  
  31.                 addedRows = 0;  
  32.                 cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);  
  33.             }  
  34.   
  35.             if (cpr == CPR_OK) {  
  36.                 addedRows += 1;  
  37.             } else if (cpr == CPR_FULL) {  
  38.                 windowFull = true;  
  39.             } else {  
  40.                 gotException = true;  
  41.             }  
  42.         } else if (err == SQLITE_DONE) {  
  43.             // All rows processed, bail   
  44.             LOG_WINDOW("Processed all rows");  
  45.             break;  
  46.         } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) {  
  47.             // The table is locked, retry   
  48.             LOG_WINDOW("Database locked, retrying");  
  49.             if (retryCount > 50) {  
  50.                 ALOGE("Bailing on database busy retry");  
  51.                 throw_sqlite3_exception(env, connection->db, "retrycount exceeded");  
  52.                 gotException = true;  
  53.             } else {  
  54.                 // Sleep to give the thread holding the lock a chance to finish   
  55.                 usleep(1000);  
  56.                 retryCount++;  
  57.             }  
  58.         } else {  
  59.             throw_sqlite3_exception(env, connection->db);  
  60.             gotException = true;  
  61.         }  
  62.     }  
  63.   
  64.     LOG_WINDOW("Resetting statement %p after fetching %d rows and adding %d rows"  
  65.             "to the window in %d bytes",  
  66.             statement, totalRows, addedRows, window->size() - window->freeSpace());  
  67.     sqlite3_reset(statement);  
  68.   
  69.     // Report the total number of rows on request.   
  70.     if (startPos > totalRows) {  
  71.         ALOGE("startPos %d > actual rows %d", startPos, totalRows);  
  72.     }  
  73.     jlong result = jlong(startPos) << 32 | jlong(totalRows);  
  74.     return result;  
  75. }  
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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值