Android数据库代码优化(2) - 从Cursor说起

Cursor说起

 

虽然大部分应用都是跟ContentProvider打交道,极少有自己的数据库需要做比如更改表结构,建索引视图之类的机会,但是对于query和操作Cursor来讲,每个同学都是非常熟悉的。我们就从Cursor说起。

 

先讨论几个在以前的开发过程中,真实出现过的问题。以此展开我们的讨论。

 

问题1Cursor要不要关?

这是某同学提出来的真实问题。理由是Cursor应该有机制自动关闭释放资源。

但是这个问题的答案是明确的,Cursor是个接口,继承自java.io.Closeable,而Closeable继承自java.lang.AutoCloseable

Android文档上明确说明,所有实现AutoCloseable接口的对象必须调用close()方法来释放资源。

原文:

Defines an interface for classes that can (or need to) be closed once they are not used any longer. Calling the close method releases resources that the object holds.

引用地址:

http://developer.android.com/reference/java/lang/AutoCloseable.html

 

问题2Cursor如何关?

这个问题在Android文档上也有明确说明,通过finally的模式来关。

原文:

A common pattern for using an AutoCloseable resource:

   Closable foo =newFoo();
   try{
      ...;
   finally{
      foo.close();
   }
 }

引文地址同上。

 

问题3Cursor中包含不包含查询的数据,是个指针还是包含实际的内容?

这也是某同学曾经提出过的疑惑。

 

答案是肯定的。Cursor中是保存了数据库数据的跨进程复制,而不是数据库内容的指针。

 

严谨一点地讲,有两种Cursor是没有数据的。一种是CursorWrapper类,它是Cursor的包装,本身没有额外包含数据。另一种是MockCursor,测试用的,并不是实际中真正使用的。

 

而实际中使用的Cursor都是实现了Cursor的子接口CrossProcessCursor接口的类。一听这个名字就有一个重要的提示,如果没数据的话,还管跨不跨进程做什么?

我们再看看CrossProcessCursor接口新增了什么方法。除了onMove回调之外,两个方法一个是fillWindow,一个是getWindow。这个Window,是CursorWindow类的对象。

这个CursorWindow类的说明再明白不过了, A buffer containing multiple cursor rows. 一个包含若干行Cursor数据的bufferCursorWindow实现了Parcelable接口,是个可以跨进程传递的对象,有了这个buffer,难怪叫CrossProcessCursor,跨进程当然是可以的。从源码中可以看到,CursorWindow类内部包含了一块匿名的共享内存。通过跨进程传递CursorWindow对象,这个共享内存的句柄也可以被访问者得到,不过拿到的只是只读权限而己。本地是可写的。

 

实现CrossProcessCursor接口的抽象类是AbstractCursor类。这个类的三个子类都是在实际中非常有用的类。

第一个类是AbstractWindowCursor类,这个抽象类最大的作用是提供了一个protected CursorWindow mWindow的对象。到这个类为止,访问者需要的东西都有了。最后的一步,是跟真正的数据库打交道。

于是,子类SQLiteCursor问世来解决跟SQLiteDatabase打交道的问题。请注意,前面的接口和类都是android.database包的,面向抽象的数据库。而SQLiteCursorandroid.database.sqlite包的,已经是针对具体的某一数据库的实现了。

SQLiteCursor的构造方法中让我们看到了两个非常感兴趣的对象,一个是SQLiteDatabase,一个是SQLiteQuery。这两个类都是后面论题的主角。

 

第二个类是MatrixCursor类。这个类的作用是用数组来冒充数据库。这个类在写ContentProvider中很有用,因为给外界的接口要是个Cursor,而数据来源不一定是数据库。比如黄页中的Provider,出于性能的原因,不是直接去数据库中查杀,而是从缓存中查,但是返回的接口要是Cursor,于是我就做了个MatrixCursor

 

第三个类是MergeCursor类。这个神奇的类的作用是将一些Cursor合并在一起。这个类的作用也很大,可以实现不容易写或者写不出来的SQL语句,或者是将不同结构的表的查询结果合并在一起。

第二种情况用于新老表结构的兼容。MergeCursor神奇之处就在于不管列名,只管merge在一起。这样,只要某列的数据是同一类型的,来自哪张表,原来的列名叫什么根本都不重要。


你可能感兴趣的:(Android数据库代码优化(2) - 从Cursor说起)