从源码看Android中sqlite是怎么读DB的

执行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并发读写数据库”。

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 }

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

时间: 10-29

从源码看Android中sqlite是怎么读DB的的相关文章

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

执行query 执行SQLiteDatabase类中query系列函数时,只会构造查询信息,不会执行查询. (query的源码追踪路径) 执行move(里面的fillwindow是真正打开文件句柄并分配内存的地方) 当执行Cursor的move系列函数时,第一次执行,会为查询结果集创建一块共享内存,即cursorwindow moveToPosition源码路径 fillWindow----真正耗时的地方 然后会执行sql语句,向共享内存中填入数据, fillWindow源码路径 在SQLite

【从源码看Android】02MessageQueue的epoll原型

1 开头 上一讲讲到Looper,大家对Looper有了大概的了结(好几个月过去了-) 大家都知道一个Handler对应有一个MessageQueue, 在哪个线程上new Handler(如果不指定looper对象),那么这个handler就默认对应于这个线程上的prepare过的Looper 如下图Handler.java代码所示,mLooper由Looper.myLooper()指定, public Handler(Callback callback, boolean async) { i

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

Android中的Veiw从内存中到呈现在UI界面上需要依次经历三个阶段:量算 -> 布局 -> 绘图,关于View的量算.布局.绘图的总体机制可参见博文< Android中View的布局及绘图机制>.量算是布局和绘图的基础,所以量算是很重要的一个环节.本文将从源码角度解析View的量算过程,这其中会涉及某些关键类以及关键方法. 对View进行量算的目的是让View的父控件知道View想要多大的尺寸. 量算过程概述 如果要进行量算的View是ViewGroup类型,那么ViewGr

深入源码解析Android中Loader、AsyncTaskLoader、CursorLoader、LoaderManager

如果对Loader.AsyncTaskLoader.CursorLoader.LoaderManager等概念不明白或不知道如何使用Loader机制,可参见博文Android中Loader及LoaderManager的使用(附源码下载).本文主要通过研究Loader及其子类的生命周期的方式来对Loader及其子类.LoaderManager的源码进行研究. Loader是靠LoaderManager管理的,LoaderManager可以同时管理多个Loader,即LoaderManager与Lo

深入源码解析Android中的Handler,Message,MessageQueue,Looper

本文主要是对Handler和消息循环的实现原理进行源码分析,如果不熟悉Handler可以参见博文< Android中Handler的使用>,里面对Android为何以引入Handler机制以及如何使用Handler做了讲解. 概括来说,Handler是Android中引入的一种让开发者参与处理线程中消息循环的机制.我们在使用Handler的时候与Message打交道最多,Message是Hanlder机制向开发人员暴露出来的相关类,可以通过Message类完成大部分操作Handler的功能.但

谈谈23种设计模式在Android源码及项目中的应用

本文首发于个人博客:Lam's Blog - 谈谈23种设计模式在Android源码及项目中的应用,文章由MarkDown语法编写,可能不同平台渲染效果不一,如果有存在排版错误图片无法显示等问题,烦请移至个人博客,如果个人博客无法访问可以留言告诉我,转载请声明个人博客出处,谢谢. 前言 本文将结合实际谈谈23种设计模式,每种设计模式涉及 * 定义:抽象化的定义与通俗的描述,尽量说明清楚其含义与应用场景 * 示例:如果项目中有使用过该模式,则会给出项目中的代码,否则会给出尽可能简单好理解的java

从源码看Azkaban作业流下发过程——记我的第一次白盒测试

一.Azkaban简介 Azkaban作为开源的调度系统,在大数据中有广泛地使用.它主要有三部分组成:Azkaban Webserver.Azkaban Executor. DB. 图1 Azkaban架构 图1所示的是Azkaban的基本架构:Webserver主要负责权限验证.项目管理.作业流下发等工作:Executor主要负责作业流/作业的具体执行以及搜集执行日志等工作:MySQL用于存储作业/作业流的执行状态信息.图中所示的是单executor场景,但是实际应用中大部分的项目使用的都是多

nginx源码分析--从源码看nginx框架总结

nginx源码总结: 1)代码中没有特别绕特别别扭的编码实现,从变量的定义调用函数的实现封装,都非常恰当,比如从函数命名或者变量命名就可以看出来定义的大体意义,函数的基本功能,再好的架构实现在编码习惯差的人实现也会黯然失色,如果透彻理解代码的实现,领悟架构的设计初衷,觉得每块代码就想经过耐心雕琢一样,不仅仅实现了基本的功能给你,为其他人阅读也会提供很好的支持.细致恰当的命名规则就可以看出作者的功力. 2)更好更高的软件性能体现在架构设计上,好的架构会让软件更加稳定.容易维护.便于扩展.从核心模块

解密随机数生成器(二)——从java源码看线性同余算法

Random Java中的Random类生成的是伪随机数,使用的是48-bit的种子,然后调用一个linear congruential formula线性同余方程(Donald Knuth的编程艺术的3.2.1节) 如果两个Random实例使用相同的种子,并且调用同样的函数,那么生成的sequence是相同的 也可以调用Math.random()生成随机数 Random实例是线程安全的,但是并发使用Random实例会影响效率,可以考虑使用ThreadLocalRandom变量. Random实