从源码看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中可以看到
@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的的更多相关文章
- 从源码看Android中sqlite是怎么读DB的(转)
执行query 执行SQLiteDatabase类中query系列函数时,只会构造查询信息,不会执行查询. (query的源码追踪路径) 执行move(里面的fillwindow是真正打开文件句柄并分 ...
- Android so 文件进阶<二> 从dlsym()源码看android 动态链接过程
0x00 前言 这篇文章其实是我之前学习elf文件关于符号表的学习笔记,网上也有很多关于符号表的文章,怎么说呢,感觉像是在翻译elf文件格式的文档一样,千篇一律,因此把自己的学习笔记分享出来.dls ...
- 源码解析Android中View的measure量算过程
Android中的Veiw从内存中到呈现在UI界面上需要依次经历三个阶段:量算 -> 布局 -> 绘图,关于View的量算.布局.绘图的总体机制可参见博文< Android中View ...
- 从源码看java中Integer的缓存问题
在开始详细的说明问题之前,我们先看一段代码 public static void compare1(){ Integer i1 = 127, i2 = 127, i3 = 128, i4 = 128; ...
- 从源码看 Vue 中的 Mixin
最近在做项目的时候碰到了一个奇怪的问题,通过 Vue.mixin 方法注入到 Vue 实例的一个方法不起作用了,后来经过仔细排查发现这个实例自己实现了一个同名方法,导致了 Vue.mixin 注入方法 ...
- 从 php 源码看 php 中的对象
从一个简单的例子说起: class Person { public $name; public $age; public function __construct($name, $age) { $th ...
- 将Android源码导入eclipse中的方法以及编译Android源码指定模块
本文博客地址:http://blog.csdn.net/qq1084283172/article/details/53365659 将android源码导入eclipse.androidstudio. ...
- 从微信小程序开发者工具源码看实现原理(一)- - 小程序架构设计
使用微信小程序开发已经很长时间了,对小程序开发已经相当熟练了:但是作为一名对技术有追求的前端开发,仅仅熟练掌握小程序的开发感觉还是不够的,我们应该更进一步的去理解其背后实现的原理以及对应的考量,这可能 ...
- 从源码看commit和commitAllowingStateLoss方法区别
Fragment介绍 在很久以前,也就是我刚开始写Android时(大约在2012年的冬天--),那时候如果要实现像下面微信一样的Tab切换页面,需要继承TabActivity,然后使用TabHost ...
随机推荐
- UIButton 头文件常见属性和方法
UIButton头文件常见属性 1.属性 contentEdgeInsets: default is UIEdgeInsetsZero.设置内容四边距,默认边距为0 @property(nonatom ...
- Address already in use: JVM_Bind<null>:8080tomcat启动不了的问题
在MyEclipse启动或者是tomcat启动的时候出现:Address already in use: JVM_Bind<null>:8080 出现该异常,这里的8080是你的端口,有可 ...
- EditText 空指针问题
今天在Android中碰到了这样一个问题,其实应该很少人会碰到,因为只有像我这种奇葩才会犯这种错误. 但既然解决了,我就想在这里跟大家分享一下,毕竟它困扰了我一个白天啊...不多说了,看下面... 其 ...
- js 概念(构造函数)
所有关于类.对象的语言里面,都有构造函数的概念,其实构造函数,就是在创建这个对象或者类的实例时候自动调用的函数,一般的语言都是new创建,那么new的参数就传递给构造函数.
- 用U盘装win7/XP系统的操作
现在上网本越来越流行了,但是上网本是没有光驱的,那如何给上网本装系统就成了一个难题,其实不仅仅不带光驱的笔记本用户愁怎么装系统,那些没有光驱的台式机用户也愁.为了给这类用户提供方便,笔者今天以上网本装 ...
- Hadoop HA部署
因为公司旧系统的Hadoop版本是2.2,所以在部署新系统时使用了旧系统. 但是在部署ResourceManager auto failover时发现其他nodemanager总是向0.0.0.0请求 ...
- Linux下 fcntl 函数用法说明
功能描述:根据文件描述词来操作文件的特性. 文件控制函数 fcntl -- file control LIBRARY Standard C Library (libc, ...
- 解决Easyui1.3.3 IE8兼容性问题
事先声明:项目在Firefox和Chrome上完美运行,在MSIE9.MSIE10上基本没问题,但是放在MSIE8上面运行问题就出来了.登录系统后,系统页面跳动,导致系统无法使用:我使用的是Easyu ...
- 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 ...
- 初识Java--线程同步(2)
本文讲述Java中的线程同步和生产者消费者问题,其中主要涉及线程同步和wait().notify()方法的用法. wait和notify方法只能用在线程同步中,wait和notify是object的方 ...