更多内容在这里查看

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

执行query

执行SQLiteDatabase类中query系列函数时,只会构造查询信息,不会执行查询。

(query的源码追踪路径)

执行move(里面的fillwindow是真正打开文件句柄并分配内存的地方)

当执行Cursor的move系列函数时,第一次执行,会为查询结果集创建一块共享内存,即cursorwindow

moveToPosition源码路径

fillWindow----真正耗时的地方

然后会执行sql语句,向共享内存中填入数据,

fillWindow源码路径

在SQLiteCursor.java中可以看到

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

追踪源码看关闭

  //SQLiteCursor

 super.close();
synchronized (this) {
mQuery.close();
mDriver.cursorClosed();
} //AbstractCursor public void close() {
mClosed = true;
mContentObservable.unregisterAll();
onDeactivateOrClose();
} protected void onDeactivateOrClose() {
if (mSelfObserver != null) {
mContentResolver.unregisterContentObserver(mSelfObserver);
mSelfObserverRegistered = false;
}
mDataSetObservable.notifyInvalidated();
} //AbstractWindowedCursor /** @hide */
@Override
protected void onDeactivateOrClose() {
super.onDeactivateOrClose();
closeWindow();
} protected void closeWindow() {
if (mWindow != null) {
mWindow.close();
mWindow = null;
}
} //SQLiteClosable public void close() {
releaseReference();
} public void releaseReference() {
boolean refCountIsZero = false;
synchronized(this) {
refCountIsZero = --mReferenceCount == 0;
}
if (refCountIsZero) {
onAllReferencesReleased();
}
} //CursorWindow @Override
protected 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:

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

从源码看Android中sqlite是怎么通过cursorwindow读DB的的更多相关文章

  1. 从源码看Android中sqlite是怎么读DB的(转)

    执行query 执行SQLiteDatabase类中query系列函数时,只会构造查询信息,不会执行查询. (query的源码追踪路径) 执行move(里面的fillwindow是真正打开文件句柄并分 ...

  2. Android so 文件进阶<二> 从dlsym()源码看android 动态链接过程

    0x00  前言 这篇文章其实是我之前学习elf文件关于符号表的学习笔记,网上也有很多关于符号表的文章,怎么说呢,感觉像是在翻译elf文件格式的文档一样,千篇一律,因此把自己的学习笔记分享出来.dls ...

  3. 源码解析Android中View的measure量算过程

    Android中的Veiw从内存中到呈现在UI界面上需要依次经历三个阶段:量算 -> 布局 -> 绘图,关于View的量算.布局.绘图的总体机制可参见博文< Android中View ...

  4. 从源码看java中Integer的缓存问题

    在开始详细的说明问题之前,我们先看一段代码 public static void compare1(){ Integer i1 = 127, i2 = 127, i3 = 128, i4 = 128; ...

  5. 从源码看 Vue 中的 Mixin

    最近在做项目的时候碰到了一个奇怪的问题,通过 Vue.mixin 方法注入到 Vue 实例的一个方法不起作用了,后来经过仔细排查发现这个实例自己实现了一个同名方法,导致了 Vue.mixin 注入方法 ...

  6. 从 php 源码看 php 中的对象

    从一个简单的例子说起: class Person { public $name; public $age; public function __construct($name, $age) { $th ...

  7. 将Android源码导入eclipse中的方法以及编译Android源码指定模块

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/53365659 将android源码导入eclipse.androidstudio. ...

  8. 从微信小程序开发者工具源码看实现原理(一)- - 小程序架构设计

    使用微信小程序开发已经很长时间了,对小程序开发已经相当熟练了:但是作为一名对技术有追求的前端开发,仅仅熟练掌握小程序的开发感觉还是不够的,我们应该更进一步的去理解其背后实现的原理以及对应的考量,这可能 ...

  9. 从源码看commit和commitAllowingStateLoss方法区别

    Fragment介绍 在很久以前,也就是我刚开始写Android时(大约在2012年的冬天--),那时候如果要实现像下面微信一样的Tab切换页面,需要继承TabActivity,然后使用TabHost ...

随机推荐

  1. UIButton 头文件常见属性和方法

    UIButton头文件常见属性 1.属性 contentEdgeInsets: default is UIEdgeInsetsZero.设置内容四边距,默认边距为0 @property(nonatom ...

  2. Address already in use: JVM_Bind<null>:8080tomcat启动不了的问题

    在MyEclipse启动或者是tomcat启动的时候出现:Address already in use: JVM_Bind<null>:8080 出现该异常,这里的8080是你的端口,有可 ...

  3. EditText 空指针问题

    今天在Android中碰到了这样一个问题,其实应该很少人会碰到,因为只有像我这种奇葩才会犯这种错误. 但既然解决了,我就想在这里跟大家分享一下,毕竟它困扰了我一个白天啊...不多说了,看下面... 其 ...

  4. js 概念(构造函数)

    所有关于类.对象的语言里面,都有构造函数的概念,其实构造函数,就是在创建这个对象或者类的实例时候自动调用的函数,一般的语言都是new创建,那么new的参数就传递给构造函数.

  5. 用U盘装win7/XP系统的操作

    现在上网本越来越流行了,但是上网本是没有光驱的,那如何给上网本装系统就成了一个难题,其实不仅仅不带光驱的笔记本用户愁怎么装系统,那些没有光驱的台式机用户也愁.为了给这类用户提供方便,笔者今天以上网本装 ...

  6. Hadoop HA部署

    因为公司旧系统的Hadoop版本是2.2,所以在部署新系统时使用了旧系统. 但是在部署ResourceManager auto failover时发现其他nodemanager总是向0.0.0.0请求 ...

  7. Linux下 fcntl 函数用法说明

    功能描述:根据文件描述词来操作文件的特性. 文件控制函数         fcntl -- file control LIBRARY         Standard C Library (libc, ...

  8. 解决Easyui1.3.3 IE8兼容性问题

    事先声明:项目在Firefox和Chrome上完美运行,在MSIE9.MSIE10上基本没问题,但是放在MSIE8上面运行问题就出来了.登录系统后,系统页面跳动,导致系统无法使用:我使用的是Easyu ...

  9. ubuntu 14.04 vnc use gnome(ubuntu14.04 gnome for vnc4server)

    New 'jenkinsmaster.cc:3 (root)' desktop is jenkinsmaster.cc:3 Starting applications specified in /ro ...

  10. 初识Java--线程同步(2)

    本文讲述Java中的线程同步和生产者消费者问题,其中主要涉及线程同步和wait().notify()方法的用法. wait和notify方法只能用在线程同步中,wait和notify是object的方 ...