Chrisの梳羽之礁

A look of quick intelligence and soft refinement
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

[转] Android数据库随笔 - 底层实现

Posted on 2011-11-09 09:54  Chrisfang6  阅读(1356)  评论(0编辑  收藏  举报

原文:Android数据库随笔

 

数据库的访问性能是很关键的问题,所以了解其底层实现,对于如何高效的使用接口函数非常有帮助。

 

SQLiteDatabase

    封装create、insert、delete、query、open、close这些数据库常用接口。SQLiteDatabase对象创建时就会调用jni dbopen去打开数据库文件,dbclose关闭数据库文件,打开与关闭时都会生成许多和释放辅助的结构,所以这是个费时的操作。

 

    数据库查询操作与写操作不同,查询操作的结果集会缓存在内存中,合理的利用缓存能够提高程序的性能。因此在SQLiteDatabase的代码中可以看到,insert、delete操作只是略微包装,就直接去操作实际数据库文件了。但查询操作却先返回Cursor给用户,没有立即访问数据库数据,而是等到真正用户使用数据时才去将数据缓存到内存中。这里的内存也只是在Native空间中。

 

SQLiteDirectCursorDriver

    顾名思义,SQLiteDirectCursorDriver是个Cursor的驱动,让真的干活的家伙跑起来。SQLiteDatabase的Query时,SQLiteDirectCursorDriver创建一个SQLiteCursor对象返回给SQLiteDatabase。

 

SQLiteCursor

    SQLiteCursor的构造函数中并没有实质性的对数据库文件访问,真正产生数据集的操作是在getCount、onMove中调用fillWindow时发生。Query调用返回Cursor对象时,并没有真正的把结果集放到内存中,而只是把结果集对应的字段缓存到mColumns[]数组中。缓存字段会对SQLiteDatabase对象上锁,并且读取字段本身的操作也是有互斥机制保护,所以在select字段不变的情况下最好复用这个cursor对象。  

    同时,这里字段"_id"的索引会保存在变量mRowIdColumnIndex中,不需要再通过getColumnIndex(String)获取。另外,如果取数据的过程是异步的话,最好调用getCount之后再将Cursor对象返回给使用者,提高使用者线程对数据集访问的性能。

    getColumnIndex中会用一个map将columnName与columnIndex的映射保存起来,所以在应用程序中缓存字段索引的做法,不会性能有明显改进。

    fillWindow最终会调用到native_fill_window,这个函数可能会返回-1,表示查询操作没有完成(不知道这么什么情况下发生的)。如果该情况发生,SQLiteCursor会产生新的线程QueryThread去完成这次查询。真正将数据从数据库文件缓存到内存的工作,都是在fillWindow中做的。

    requery提供了Cursor对象重用的方法。重新查询最多只能够修改查询条件,就是where表达的过滤条件,在requery之前调用setSelectionArguments修改;如果过滤条件不变,则直接调用requery即可。requery中只是做变量的复位操作并更新jni层的查询条件。真正的将数据存入结果集的操作,要等到第一次调用getCount、onMove、moveToPosition操作。

 

 

CursorWindow

    CursorWindow就是存放结果集的地方,这个类实际是C层CursorWindow对象的外壳,nWindow存放C层CursorWindow对象地址。也就是说,数据并没有复制到Java空间,而是保存Native空间,要用的时候才传上来。

 

SQLiteQuery

    这里就是Java与C的交界处,SQLiteQuery封装对Query Jni的访问。成员函数fillWindow中调用jni native_fill_window,在C层将数据填到CursorWindow对象中。