標籤:
執行query
執行SQLiteDatabase類中query系列函數時,只會構造查詢資訊,不會執行查詢。
(query的源碼追蹤路徑)
執行move(裡面的fillwindow是真正開啟檔案控制代碼並分配記憶體的地方)
當執行Cursor的move系列函數時,第一次執行,會為查詢結果集建立一塊共用記憶體,即cursorwindow
moveToPosition源碼路徑
fillWindow----真正耗時的地方
然後會執行sql語句,向共用記憶體中填入資料,
fillWindow源碼路徑
在SQLiteCursor.java中可以看到
1 @Override 2 public boolean onMove(int oldPosition, int newPosition) { 3 // Make sure the row at newPosition is present in the window 4 if (mWindow == null || newPosition < mWindow.getStartPosition() || 5 newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) { 6 fillWindow(newPosition); 7 } 8 9 return true;10 }
如果請求查詢的位置在cursorWindow的範圍內,不會執行fillWindow,
而超出cursorwindow的範圍,會調用fillWindow,
而在nativeExecuteForCursorWindow中,
擷取記錄時,如果要請求的位置超出視窗範圍,會發生CursorWindow的清空:
1 CopyRowResult cpr = copyRow(env, window, statement, numColumns, startPos, addedRows); 2 if (cpr == CPR_FULL && addedRows && startPos + addedRows < requiredPos) { 3 // We filled the window before we got to the one row that we really wanted. 4 // Clear the window and start filling it again from here. 5 // TODO: Would be nicer if we could progressively replace earlier rows. 6 window->clear(); 7 window->setNumColumns(numColumns); 8 startPos += addedRows; 9 addedRows = 0; 10 cpr = copyRow(env, window, statement, numColumns, startPos, addedRows); 11 }
CursorWindow的清空機制會影響到多線程讀(通常認為不可以並發讀寫,sqlite的並發實際上是串列執行的,但可以並發讀,這裡要強調的是多線程讀也可能有問題),具體見稍後一篇文章“listview並發讀寫資料庫”。
Cursor
關閉(顯式調用close()的理由)
追蹤源碼看關閉
1 //SQLiteCursor 2 3 super.close(); 4 synchronized (this) { 5 mQuery.close(); 6 mDriver.cursorClosed(); 7 } 8 9 10 //AbstractCursor11 12 public void close() {13 mClosed = true;14 mContentObservable.unregisterAll();15 onDeactivateOrClose();16 }17 18 protected void onDeactivateOrClose() {19 if (mSelfObserver != null) {20 mContentResolver.unregisterContentObserver(mSelfObserver);21 mSelfObserverRegistered = false;22 }23 mDataSetObservable.notifyInvalidated();24 }25 26 27 //AbstractWindowedCursor28 29 /** @hide */30 @Override31 protected void onDeactivateOrClose() {32 super.onDeactivateOrClose();33 closeWindow();34 }35 36 protected void closeWindow() {37 if (mWindow != null) {38 mWindow.close();39 mWindow = null;40 }41 }42 43 44 45 //SQLiteClosable46 47 public void close() {48 releaseReference();49 }50 51 public void releaseReference() {52 boolean refCountIsZero = false;53 synchronized(this) {54 refCountIsZero = --mReferenceCount == 0;55 }56 if (refCountIsZero) {57 onAllReferencesReleased();58 }59 }60 61 //CursorWindow62 63 @Override64 protected void onAllReferencesReleased() {65 dispose();66 }67 68 private void dispose() {69 if (mCloseGuard != null) {70 mCloseGuard.close();71 }72 if (mWindowPtr != 0) {73 recordClosingOfWindow(mWindowPtr);74 nativeDispose(mWindowPtr);75 mWindowPtr = 0;76 }77 }View Code
跟CursorWindow有關的路徑裡,最終調用nativeDispose()清空cursorWindow;
當Cursor被GC回收時,會調用finalize:
1 @Override 2 protected void finalize() { 3 try { 4 // if the cursor hasn‘t been closed yet, close it first 5 if (mWindow != null) { 6 if (mStackTrace != null) { 7 String sql = mQuery.getSql(); 8 int len = sql.length(); 9 StrictMode.onSqliteObjectLeaked(10 "Finalizing a Cursor that has not been deactivated or closed. " +11 "database = " + mQuery.getDatabase().getLabel() +12 ", table = " + mEditTable +13 ", query = " + sql.substring(0, (len > 1000) ? 1000 : len),14 mStackTrace);15 }16 close();17 }18 } finally {19 super.finalize();20 }21 }
然而finalize()並沒有釋放CursorWindow,而super.finalize();裡也只是解除綁定了觀察者,沒有去釋放cursorwindow
所以不調用cursor.close(),最終會導致cursorWindow所在的共用記憶體(1M或2M)泄露。
從源碼看Android中sqlite是怎麼讀DB的