cursor.moveToNext()会出异常,如下
E/AndroidRuntime( 2249): FATAL EXCEPTION: Thread-49
E/AndroidRuntime( 2249): java.lang.IllegalStateException: Cannot perform this operation because the connection pool has been closed.
E/AndroidRuntime( 2249): at android.database.sqlite.SQLiteConnectionPool.throwIfClosedLocked(SQLiteConnectionPool.java:962)
E/AndroidRuntime( 2249): at android.database.sqlite.SQLiteConnectionPool.waitForConnection(SQLiteConnectionPool.java:599)
E/AndroidRuntime( 2249): at android.database.sqlite.SQLiteConnectionPool.acquireConnection(SQLiteConnectionPool.java:348)
E/AndroidRuntime( 2249): at android.database.sqlite.SQLiteSession.acquireConnection(SQLiteSession.java:894)
E/AndroidRuntime( 2249): at android.database.sqlite.SQLiteSession.executeForCursorWindow(SQLiteSession.java:834)
E/AndroidRuntime( 2249): at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:62)
E/AndroidRuntime( 2249): at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:143)
E/AndroidRuntime( 2249): at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:133)
E/AndroidRuntime( 2249): at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:197)
E/AndroidRuntime( 2249): at android.database.AbstractCursor.moveToNext(AbstractCursor.java:245)
解决办法,调用cursor.getCount().
原因大概如下:
当我们第一调用android.database.sqlite.SQLiteCursor的getCount()时,当前线程会锁定数据库,在该操作完成后才解锁。
其调用关系如下:
at android.database.sqlite.SQLiteQuery.native_fill_window(Native Method)
at android.database.sqlite.SQLiteQuery.
fillWindow(
SQLiteQuery.java:73)
at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:287)
at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:268)
at android.widget.CursorAdapter.
getCount
(CursorAdapter.java:132)
如果是第一次调用
SQLiteCursor
的
getCount
()
的话,在getCount()中,它会调用
fillWindow
(),
在SQLiteCursor的
fillWindow()
中,它又会调用SQLiteQuery的
fillWindow()
android.database.sqlite.SQLiteCursor
的相关
源码如下: @Override
public int getCount() {
if (mCount == NO_COUNT) {
fillWindow(0); }
return mCount; }
private void fillWindow (
int startPos) {
if (mWindow == null) { // If there isn’t a window set already it will only be accessed locally mWindow = new CursorWindow(true /* the window is local only */); }
else { mCursorState++; queryThreadLock(); try { mWindow.clear(); } finally { queryThreadUnlock(); } } mWindow.setStartPosition(startPos); mCount =
mQuery.fillWindow(mWindow, mInitialRead, 0); // return -1 means not finished
if (mCount == NO_COUNT){ mCount = startPos + mInitialRead; Thread t = new Thread(new QueryThread(mCursorState), “query thread”); t.start(); } }
在SQLiteQuery的
fillWindow()
中,它首先需要
lock数据库
,然后调用JNI层的
native_fill_window()
进行数据库操作,在其操作完成之后才
unlock数据库
。
android.database.sqlite.SQLiteQuery
的相关源码如下:
/**
* Reads rows into a buffer. This method acquires the database lock.
*
* @param window The window to fill into
* @return number of total rows in the query
*/
int fillWindow(CursorWindow window,
int maxRead,
int lastPos) {
long timeStart = SystemClock.uptimeMillis();
mDatabase.lock(); mDatabase.logTimeStat(mSql, timeStart, SQLiteDatabase.GET_LOCK_LOG_PREFIX);
try { acquireReference();
try { window.acquireReference();
// if the start pos is not equal to 0, then most likely window is
// too small for the data set, loading by another thread
// is not safe in this situation. the native code will ignore maxRead
int numRows =
native_fill_window(window, window.getStartPosition(), mOffsetIndex, maxRead, lastPos);
// Logging
if (SQLiteDebug.DEBUG_SQL_STATEMENTS) { Log.d(TAG, “fillWindow(): ” + mSql); } mDatabase.logTimeStat(mSql, timeStart); return numRows; }
catch (IllegalStateException e){
// simply ignore it return 0; }
catch (SQLiteDatabaseCorruptException e) { mDatabase.onCorruption(); throw e; }
finally { window.releaseReference(); } } finally { releaseReference();
mDatabase.unlock(); } }
结束!