从源码看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中可以看到

@Overridepublic boolean onMove(int oldPosition, int newPosition) {    // Make sure the row at newPosition is present in the window    if (mWindow == null || newPosition < mWindow.getStartPosition() ||            newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) {        fillWindow(newPosition);    }    return true;}

如果请求查询的位置在cursorWindow的范围内,不会执行fillWindow,

而超出cursorwindow的范围,会调用fillWindow,

而在nativeExecuteForCursorWindow中,

获取记录时,如果要请求的位置超出窗口范围,会发生CursorWindow的清空:

CopyRowResult cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);  if (cpr == CPR_FULL && addedRows && startPos + addedRows < requiredPos) {  // We filled the window before we got to the one row that we really wanted. // Clear the window and start filling it again from here. // TODO: Would be nicer if we could progressively replace earlier rows. window->clear();  window->setNumColumns(numColumns);  startPos += addedRows;  addedRows = 0;  cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);  }

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()的理由)

追踪源码看关闭

 //SQLiteCursorsuper.close();synchronized (this) {    mQuery.close();    mDriver.cursorClosed();}//AbstractCursorpublic void close() {    mClosed = true;    mContentObservable.unregisterAll();    onDeactivateOrClose();}protected void onDeactivateOrClose() {    if (mSelfObserver != null) {        mContentResolver.unregisterContentObserver(mSelfObserver);        mSelfObserverRegistered = false;    }    mDataSetObservable.notifyInvalidated();}//AbstractWindowedCursor/** @hide */@Overrideprotected void onDeactivateOrClose() {    super.onDeactivateOrClose();    closeWindow();}protected void closeWindow() {    if (mWindow != null) {        mWindow.close();        mWindow = null;    }}//SQLiteClosablepublic void close() {    releaseReference();}public void releaseReference() {    boolean refCountIsZero = false;    synchronized(this) {        refCountIsZero = --mReferenceCount == 0;    }    if (refCountIsZero) {        onAllReferencesReleased();    }}//CursorWindow@Overrideprotected void onAllReferencesReleased() {    dispose();}private void dispose() {    if (mCloseGuard != null) {        mCloseGuard.close();    }    if (mWindowPtr != 0) {        recordClosingOfWindow(mWindowPtr);        nativeDispose(mWindowPtr);        mWindowPtr = 0;    }}

跟CursorWindow有关的路径里,最终调用nativeDispose()清空cursorWindow;

当Cursor被GC回收时,会调用finalize:

@Overrideprotected void finalize() {    try {        // if the cursor hasn't been closed yet, close it first        if (mWindow != null) {            if (mStackTrace != null) {                String sql = mQuery.getSql();                int len = sql.length();                StrictMode.onSqliteObjectLeaked(                    "Finalizing a Cursor that has not been deactivated or closed. " +                    "database = " + mQuery.getDatabase().getLabel() +                    ", table = " + mEditTable +                    ", query = " + sql.substring(0, (len > 1000) ? 1000 : len),                    mStackTrace);            }            close();        }    } finally {        super.finalize();    }}

然而finalize()并没有释放CursorWindow,而super.finalize();里也只是解绑了观察者,没有去释放cursorwindow

所以不调用cursor.close(),最终会导致cursorWindow所在的共享内存(1M或2M)泄露。

更多相关文章

  1. android:largeHeap
  2. Ubuntu 14搭建源码Android编译环境和Android(安卓)Studio开发环
  3. Android2.2添加busybox 支持——基于Android(安卓)Bionic库
  4. android开发常用工具类、高仿客户端、附近厕所、验证码助手、相
  5. Android前端RxJava2+Retrofit2;后端SpringMvc实现图片上传
  6. Java环境配置和Android(安卓)Studio的安装教程
  7. [React-Native]RN组件学习-Image
  8. 我的android 第23天 - UriMatcher类使用介绍
  9. 【转】Android(安卓)分析内存的使用情况

随机推荐

  1. RabbitMQ基本概念和原理实例
  2. 介绍一个微软开源项目网站--CodePlex
  3. 软件的增量更新是什么?
  4. 把exe注册为windows服务实例教程
  5. WPF下使用Uri的实例教程
  6. 网络通讯控制器分组,提高交互的负载平衡能
  7. .NET中怎么实现程序分页
  8. C#中五种访问修饰符作用范围实例详解
  9. 关于json result的实例代码
  10. H5结合百度map实现GPS定位的实例教程