Android系统方便应用使用,在Frameworks层中封装了一套Content框架,之因此叫Content框架而不叫数据库框架之类的,是由于这里Content不必定是来自数据库的内容,也能够是来自其余数据源的内容,开发人员只须要知道如何使用ContentResovler和ContentProvider就能够在应用进程之间共享数据了。java
这里咱们只讨论数据源是数据库的ContentProvider,开发人员须要实现一个SQLiteOpenHelper的派生类,它使用了一系列SQLite相关的类封装了native层的SQLite动态库的接口方法,那么SQLite在Frameworks层是如何封装的,咱们在使用SQLite时又须要注意些什么呢?android
咱们先来看一看基于SQLite的Content框架的总体架构:sql
Android系统在Frameworks层数据库
(1)不论是调用getWritableDatabase方法仍是getReadableDatabase方法,SQLIteOpenHelper都会以可读写模式打开数据库。编程
(2)若是应用程序想以WAL模式打开数据库,可在自定义SQLiteOpenHelper类的构造方法中调用setWriteAheadLoggingEnabled(true)。 设计模式
SQLiteOpenHelper.java 架构
六、SQLiteConnectionPool:数据库链接池,管理全部打开的数据库链接(Connection)。全部数据库链接都是经过它来打开,打开后会加入链接池,在读写数据库时须要从链接池中获取一个数据库链接来使用。 并发
七、SQLiteConnection:表明了数据库链接,每一个Connection封装了一个native层的sqlite3实例,经过JNI调用SQLite动态库的接口方法操做数据库,Connection要么被Session持有,要么被链接池持有。 框架
八、CursorFactory:可选的Cursor工厂,能够提供自定义工厂来建立Cursor。 ide
九、DatabaseErrorHandler:可选的数据库异常处理器(目前仅处理数据库Corruption),若是不提供,将会使用默认的异常处理器。
十、SQLiteDatabaseConfiguration:数据库配置,应用程序能够建立多个到SQLite数据库的链接,这个类用来保证每一个链接的配置都是相同的。
十一、SQLiteQuery和SQLiteStatement:从抽象类SQLiteProgram派生,封装了SQL语句的执行过程,在执行时自动组装待执行的SQL语句,并调用SQLiteSession来执行数据库操做。这两个类的实现应用了设计模式中的命令模式。
本节介绍几个关键模块的实现和使用时须要注意的事项。
Android系统Frameworks层的数据库读写操做都是经过SQLiteSession完成的,SQLiteSession负责管理数据库链接和事务的生命周期。
一个SQLiteDatabse实例能够同时持有多个活跃的Session(可是为防止死锁,每一个线程只能持有一个DB的Session),每一个Session在执行SQL语句时获取数据库链接,在SQL语句执行结束后释放数据库链接,Session只有在只执行SQL语句期间保持数据库链接,执行完后就释放了。这个特性也是链接池的实现基础。
SQLiteSession.java
若是链接池中全部链接都已分配出去了,那么获取链接的SQLiteSession会阻塞直到有可用链接为止。
因此,在使用SQLite时须要注意如下几点:
(2)执行的事务尽可能短。
(3)若是读写事务很长,能够考虑使用yieldTransaction()方法先提交部分事务,给其余事务执行的机会。
数据库链接池保持全部打开的数据库链接,在任什么时候候,一个数据库链接要么被链接池持有,要么被一个SQLiteSession持有,若是SQLiteSession使用完数据库链接,必须把它还给链接池。若是链接池中全部的链接都已被占用,则待执行的事务要等待有空闲的链接才能执行。
目前Android系统的实现中,若是以非WAL模式打开数据库,链接池中只会保持一个数据库链接,若是以WAL模式打开数据库,链接池中的最大链接数量则根据系统配置决定,默认配置是两个。
SQLiteConnectionPool.java:
虽然名为链接池,可是从源码来看,目前实现的池中只有一个数据库链接(之后的Android版本可能会扩展),因此若是应用程序中有大量的并发数据库读和写操做的话,每一个操做的时长均可能受到影响,因此数据库操做应放在工做线程中执行,以避免影响UI响应。
从ContentProvider查询的数据结果是放在Cursor中返回给客户端的,在客户端看来Cursor就是一个数据容器,但隐藏在Cursor后面的实现方式很灵活,它的数据既能够不是从数据库返回的,也能够是在使用时才真正加载的,很好的体现了面向对象编程的特性和优势。
在Frameworks中把Cursor定义为了一个接口,它的定位是能够随机访问的数据集,在接口中定义了访问数据集的通用方法。业务能够根据本身的须要实现一个Cursor,具体怎么实现接口的方法由具体实现决定,因此Cursor有不少子类来知足不一样场景的须要。
经过SQLiteDatabase返回的就是其中的SQLiteCursor,若是数据不是从数据库返回的,开发人员也能够在ContentProvider中动态建立一个MatrixCursor,而后填充数据并返回给客户端。
从Cursor接口和其派生类的定义来看它们都没有实现Parcelable接口,那么它是怎么跨进程传递的呢?这须要Cursor首先要解决两个问题。
第一个问题:Cursor没有实现Parcelable接口,一个Cursor实例怎么跨进称传递呢?答案是传递的不是具体数据,而是Binder引用,即在ContentProvider端建立Cursor的Binder服务端实例,而后把Binder应用传递给客户端,在客户端经过这个Binder引用跨进程获取查询到的数据的。这里Frameworks定义了一个接口:IBulkCursor。
IBulkCursor定义了跨进程的Cursor须要实现的接口方法,其中getWindow()用来得到数据窗口,onMove()用来移动Cursor的位置。
那么第二个问来了:咱们知道经过Binder传递的数据大小有限(1MB),而查询到的数据大小可能超出限制,那么怎么跨进程传递数据呢?既然数据大小不定,那么咱们就不经过Binder传递数据了,而是经过共享内存传递数据,这块共享内存是封装在CursorWindow中的。
CursorWindow就是数据窗口,它在服务端分配(窗口大小有Android系统配置决定)并传递到客户端,客户端再映射到本身的进程空间中,这样,服务端填充的数据就能够被客户端读取到了。上面IBulkCursor接口中定义的getWindow()方法就是获取CursorWindow的。
CursorWindow在初始化时是空的,在调用Cursor的moveToXXX方法时会经过IBulkCursor的onMove()方法调用服务端的Cursor去填充数据窗口的内容。
CursorWindow.java
frameworks\base\libs\androidfw\CursorWindow.cpp
服务端建立共享内存。
CursorWindow.java
frameworks\base\libs\androidfw\CursorWindow.cpp
客户端映射共享内存到进程内存空间。
上面知道了Frameworks解决跨进程传递Cursor数据的思路,咱们再来看下具体执行跨进程传递数据的类:CursorToBulkCursorAdapter(服务端)和BulkCursorToCursorAdapter(客户端)。
服务端:
客户端:
在ContentProviderNatvie类中能够看到Cursor的专递过程。
服务端:
ContentProviderNative.onTransact()
CursorToBulkCursorAdaptor.java
服务端在经过ContentProvider获得Cursor后,用它建立一个CursorToBulkCursorAdaptor实例,而后把adaptor封装在一个实现了Parcelable接口的BulkCursorDescriptor实例中返回给客户端。
虽然说是在使用时才填充数据窗口,可是实际上传递Cursor的过程当中,从上面代码能够看到服务端已经替应用程序填充过一次数据了:mCursor.getCount()。
SQLiteCursor.java
客户端:
ContentProviderProxy.query()
BulkCursorToCursorAdaptor.java
在获得服务端返回的数据后建立一个BulkCursorDescriptor实例,在用它初始化一个BulkCursorToCursorAdapter实例返回给应用程序使用。