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)泄露。

  • 相关阅读:
    数组中的逆序对 --剑指offer
    第一个只出现一次的字符 --剑指offer
    丑数 --剑指offer
    把数组排成最小的数 --剑指offer
    整数中1出现的次数 --剑指offer
    最小的k个数 --剑指offer
    数组中出现次数超过一半的数字 --剑指offer
    redis击穿,穿透,雪崩,分布式锁,api(jedis,luttuce)
    Java创建数据库新建表及初始化表
    generatorConfig.xml自动生成实体类,dao和xml
  • 原文地址:https://www.cnblogs.com/hellocwh/p/4924732.html
Copyright © 2011-2022 走看看