從源碼看Android中sqlite是怎么通過cursorwindow讀DB的


 

更多內容在這里查看

 

https://ahangchen.gitbooks.io/windy-afternoon/content/

 

執行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並發讀寫數據庫”。

 上面說的這些直觀的感受是什么樣的呢?大概是這樣,

  執行query,讀10000條數據,很快就拿到了cursor,這里不會卡,

  執行moveToFirst,卡一下(fillwindow(0))

  moveToPosition(7500),卡一下,因為已經超了cursorwindow的區域,又去fillwindow(7500),

  關於fillwindow還有一些奇特的細節,比如4.0以后,fillwindow會填充position前后各一段數據,防止讀舊數據的時候又需要fill,感興趣的同學可以看看各個版本fillwidow的源碼。

  這里還可以延伸一下,因為高版本的android sqlite對舊版有許多改進,

  所以實際開發里我們有時候會把sqlite的源碼帶在自己的工程里,使得低版本的android也可以使用高版本的特性,並且避開一部分兼容性問題。

Cursor關閉(顯式調用close()的理由)

追蹤源碼看關閉

 1  //SQLiteCursor
 2 
 3 super.close();
 4 synchronized (this) {
 5     mQuery.close();
 6     mDriver.cursorClosed();
 7 }
 8 
 9 
10 //AbstractCursor
11 
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 //AbstractWindowedCursor
28 
29 /** @hide */
30 @Override
31 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 //SQLiteClosable
46 
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 //CursorWindow
62 
63 @Override
64 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)泄露。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM