从源码看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 windowif (mWindow == null || newPosition < mWindow.getStartPosition() ||newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) {fillWindow(newPosition);}return true;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
如果请求查询的位置在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);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 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()的理由)
追踪源码看关闭
//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 */
@Override
protected 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@Override
protected void onAllReferencesReleased() {dispose();
}private void dispose() {if (mCloseGuard != null) {mCloseGuard.close();}if (mWindowPtr != 0) {recordClosingOfWindow(mWindowPtr);nativeDispose(mWindowPtr);mWindowPtr = 0;}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
跟CursorWindow有关的路径里,最终调用nativeDispose()清空cursorWindow;
当Cursor被GC回收时,会调用finalize:
@Override
protected void finalize() {try {// if the cursor hasn't been closed yet, close it firstif (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();}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
然而finalize()并没有释放CursorWindow,而super.finalize();里也只是解绑了观察者,没有去释放cursorwindow
所以不调用cursor.close(),最终会导致cursorWindow所在的共享内存(1M或2M)泄露。
原文地址http://blog.csdn.net/u011453773/article/details/50731250
从源码看ANDROID中SQLITE是怎么通过CURSORWINDOW读DB的相关推荐
- dlsym 如何查看一个so里面的_Android so 文件进阶二 从dlsym()源码看android 动态链接过程...
0x00 前言 这篇文章其实是我之前学习elf文件关于符号表的学习笔记,网上也有很多关于符号表的文章,怎么说呢,感觉像是在翻译elf文件格式的文档一样,千篇一律,因此把自己的学习笔记分享出来.dls ...
- 从源码解析-Android中Zygote进程是如何fork一个APP进程的
zygote进程fork子进程 前言 强烈推荐 进程创建流程 APP启动第三方应用 startActivity startService sendBroadcast ContentResolver.q ...
- 从源码解析-Android中View的绘制流程及performTraversals方法
谈谈Activity的setContentView是怎么加载XML视图的 谈谈Activity的View怎么与View绘制工具ViewRootImpl关联的 在前面两篇文章中分析了View是如何跟绘制 ...
- 从源码看DL4J中Native BLAS的加载,以及配置
最近在用DeepLearning4J(DL4J)尝试语音识别的深度学习,git DL4J的代码,用IntelliJ IDEA打开,配置好相关依赖后,运行包org.deeplearning4j.exam ...
- 【Android 安装包优化】p7zip 源码交叉编译 Android 平台可执行程序 ( 下载 p7zip 源码 | 交叉编译 Android 中使用 7z 可执行程序 )
文章目录 一.下载 p7zip 源码 二.交叉编译 Android 中使用 7z 可执行程序 三.参考资料 一.下载 p7zip 源码 下载 7zip 源码 , 下载页面 https://source ...
- 从AndFix源码看Android热修复
前几篇文章介绍过注入Dex实现热修复:http://blog.csdn.net/u011686167/article/details/78966936 .现在探讨阿里系的底层替换虚拟机的方法指针实现热 ...
- android 代码 drawable,Android Drawable完全解析(一):Drawable源码分析(中)
呃...我不是故意要凑篇幅写个什么上下篇,实在是因为Drawable源码有点长,一篇写不下啦O(∩_∩)O~ 鉴于源码一般较长,以后所有源码分析的部分,英文注释非必要情况都不再保留! 2:Drawab ...
- 结合源码探讨Android系统的启动流程
结合源码探讨Android系统的启动流程 由于本人能力有限,所考虑或者疏忽错漏的地方或多或少应该存在.同时,Android从启动过程开始,实际上就涉及多个技术难点和多种通信机制的知识点. 基于上面两个 ...
- 视频直播源码在Android端实现1对1音视频实时通话
我们要使用 WebRTC 进行音视频互动时需要申请访问硬件的权限,至少要申请以下三种权限 Camera 权限 Record Audio 权限 Intenet 权限 在Android中,申请权限分为静态 ...
最新文章
- Kotlin 类的定义
- java防止用户越权访问文件_针对功能权限(url访问)如何避免越权访问
- 实现windows标准的选择文件夹功能
- rep movsd + rep movsb 内联实现 strcpy
- 【人物】徐小平:远离创业的3个死亡陷阱
- 使用websockets,后台实时发数据,前台实时接受数据,并集成到Django
- OpenCV视频进度播放控制
- PreparedStatement跟Statement的对比
- 智能玩具 数据采集 首页展示 注册 登录 自动登录 二维码图片
- 【老军医方】在脱发过程中遇到的各种疑难杂症
- WIFI抓包理论篇——802.11帧与EthernetII帧的差别
- marlab中主成分得分怎么求_数学无耻得分法,独家秘密,快点用起来
- esp8266开发入门教程(基于Arduino)——环境安装
- Redis 3种集群方式,别傻傻分不清!
- 耳麦没声音,耳麦不能说话
- Shave Beaver! CodeForces - 331B2 (线段树)
- Clickhouse 各种工具函数知识 -<日期函数>
- 中国移动号码手机开机以及注册gprs流程(转载)
- 物联网技能大赛-Ubuntu-(4)
- python numpy.arry, pytorch.Tensor及原生python中list相互转换
热门文章
- 雷达模拟器-监控摄像机模拟软件 SPx Video Simulator
- A-level 课程:最受欢迎和最不受欢迎的学科
- pyinstaler打包paddle
- Java动态性——反射机制学习笔记
- 轻便易用的三维建模软件
- canvas绘制火柴人
- Django报错 ValueError: The view didn‘t return an HttpResponse object. It returned None instead.
- 如何自定义您的Nintendo Switch主屏幕
- 揭秘:雷电模拟器工作方式
- 深井泵房无人值守系统 泵站无人值守平台 智慧水务