zoukankan      html  css  js  c++  java
  • 从源码看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)泄露。

  • 相关阅读:
    codeforces C. No to Palindromes!
    codeforces D. Pashmak and Parmida's problem
    codeforces C. Little Pony and Expected Maximum
    codeforces D. Count Good Substrings
    codeforces C. Jzzhu and Chocolate
    codeforces C. DZY Loves Sequences
    codeforces D. Multiplication Table
    codeforces C. Painting Fence
    hdu 5067 Harry And Dig Machine
    POJ 1159 Palindrome
  • 原文地址:https://www.cnblogs.com/hellocwh/p/4924732.html
Copyright © 2011-2022 走看看