本节将分析MediaProvider query函数。此次分析的焦点不是MediaProvider本身的业务逻辑,而是要搞清query函数返回的Cursor到底是什么,其代码如下,:
**MediaProvider.java::query**
~~~
public Cursor query(Uri uri, String[]projectionIn, String selection,
String[] selectionArgs, String sort) {
int table= URI_MATCHER.match(uri);
......
//根据uri取出对应的DatabaseHelper对象,MediaProvider针对内部存储中的媒体文件和
//外部存储(即SD卡)中的媒体文件分别创建了两个数据库
DatabaseHelper database = getDatabaseForUri(uri);
//我们在7.3.2节分析过getReadableDatabase函数的兄弟getWritableDatabase函数
SQLiteDatabase db = database.getReadableDatabase();
//创建一个SQLiteQueryBuilder对象,用于方便开发人员编写SQL语句
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
......//设置qb的参数,例如调用setTables函数为本次查询设定目标Table
//①调用SQLiteQueryBuilder的query函数
Cursor c =qb.query(db, projectionIn, selection,
combine(prependArgs, selectionArgs), groupBy, null, sort, limit);
if (c !=null) {
//2调用该Cursor对象的setNotificationUri函数,这部分内容和ContentObserver
//有关,相关内容留到第8章再进行分析
c.setNotificationUri(getContext().getContentResolver(), uri);
}
return c;
}
~~~
上边代码列出的两个关键点分别是:
- 调用SQLiteQueryBuilder的query函数得到一个Cursor类型的对象。
- 调用Cursor类型对象的setNotificationUri函数。从名字上看,是为该对象设置通知URI。和ContentObserver有关的内容留到
第8章再进行分析。
来看SQLiteQueryBuilder的query函数。
1. SQLiteQueryBuilder的query函数分析
**SQLiteQueryBuilder.java::query**
~~~
public Cursor query(SQLiteDatabase db, String[]projectionIn,
String selection, String[] selectionArgs, String groupBy,
String having, String sortOrder, String limit) {
......
//调用buildQuery函数得到对应SQL语句的字符串
String sql= buildQuery(
projectionIn, selection, groupBy, having,
sortOrder, limit);
/*
调用SQLiteDatbase的rawQueryWithFactory函数,mFactory是SQLiteQueryBuilder
的成员变量,初始值为null,本例也没有设置它,故mFactory为null
*/
returndb.rawQueryWithFactory(
mFactory, sql, selectionArgs,
SQLiteDatabase.findEditTable(mTables));
}
~~~
**SQLiteDatabase.java::rawQueryWithFactory**
~~~
public Cursor rawQueryWithFactory(
CursorFactory cursorFactory, String sql, String[] selectionArgs,
StringeditTable) {
verifyDbIsOpen();
BlockGuard.getThreadPolicy().onReadFromDisk();
//数据库开发中经常碰到连接池的概念,其目的也是缓存重型资源。有兴趣的读者不妨自行
//研究一下面这个getDbConnection函数
SQLiteDatabase db = getDbConnection(sql);
//创建一个SQLiteDirectCursorDriver对象
SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(
db, sql,editTable);
Cursorcursor = null;
try {
//调用SQLiteCursorDriver的query函数
cursor = driver.query(
cursorFactory != null ? cursorFactory : mFactory,
selectionArgs);
}finally {
releaseDbConnection(db);
}
returncursor;
~~~
以上代码中又出现一个新类型,即SQLiteCursorDriver,cursor变量是其query函数的返回值。
(1) SQLiteCursorDriver query函数分析
SQLiteCursorDriverquery函数的代码如下:
**SQLiteCursorDriver.java::query**
~~~
public Cursor query(CursorFactory factory,String[] selectionArgs) {
//本例中,factory为空
SQLiteQuery query = null;
try {
mDatabase.lock(mSql);
mDatabase.closePendingStatements();
//①构造一个SQLiteQuery对象
query = new SQLiteQuery(mDatabase, mSql, 0, selectionArgs);
//原来,最后返回的游标对象其真实类型是SQLiteCursor
if(factory == null) {//②构造一个SQLiteCursor对象
mCursor = new SQLiteCursor(this, mEditTable, query);
}else {
mCursor =factory.newCursor(mDatabase, this,
mEditTable, query);
}
mQuery = query;
query = null;
return mCursor;//原来返回的cursor对象其真实类型是SQLiteCursor
}finally {
if (query != null) query.close();
mDatabase.unlock();
}
}
~~~
SQLiteCursorDriver的query函数的主要功能就是创建一个SQLiteQuery实例和一个SQLiteCursor实例。至此,我们终于搞清楚了MediaProvider 的query返回的游标对象其真实类型是SQLiteCursor。
* * * * *
**注意**:这里的MediaProvider query指的是图7-5中的编号为8关键调用。
* * * * *
下面来看SQLiteQuery和SQLiteCursor为何方神圣。
(2) SQLiteQuery介绍
在图7-4中曾介绍过SQLiteQuery,它保存了和查询(即SQL的SELECT命令)命令相关的信息,其构造函数的代码如下:
**SQLiteQuery.java::构造函数**
~~~
SQLiteQuery(SQLiteDatabase db, String query, intoffsetIndex,
String[] bindArgs) {
//注意,在本例中offsetIndex为0,offsetIndex的意义到后面再解释
super(db,query);//调用基类构造函数
mOffsetIndex = offsetIndex;
bindAllArgsAsStrings(bindArgs);
}
~~~
SQLiteQuery的基类是SQLiteProgram,其代码如下:
**SQLiteProgram.java::构造函数**
~~~
SQLiteProgram(SQLiteDatabase db, String sql) {
this(db,sql, null, true);//调用另外一个构造函数,注意传递的参数
}
SQLiteProgram(SQLiteDatabase db, String sql,Object[] bindArgs,
boolean compileFlag) {
//本例中bindArgs为null,compileFlag为true
mSql =sql.trim();
//返回该SQL语句的类型,query语句将返回STATEMENT_SELECT类型
int n =DatabaseUtils.getSqlStatementType(mSql);
switch(n) {
caseDatabaseUtils.STATEMENT_UPDATE:
mStatementType = n | STATEMENT_CACHEABLE;
break;
caseDatabaseUtils.STATEMENT_SELECT:
/*
mStatementType成员变量用于标示该SQL语句的类型,如果该SQL语句
是STATEMENT_SELECT类型,则mStatementType将设置STATEMENT_CACHEABLE
标志。该标志表示此对象将被缓存起来,以避免再次执行同样的SELECT命令时重新构造
一个对象
*/
mStatementType = n | STATEMENT_CACHEABLE |
STATEMENT_USE_POOLED_CONN;
break;
......//其他情况处理
default:
mStatementType = n;
}
db.acquireReference();//增加引用计数
db.addSQLiteClosable(this);
mDatabase= db;
nHandle =db.mNativeHandle;//此mNativeHandle对应一个SQLite3实例
if(bindArgs != null) ......//绑定参数
//complieAndBindAllArgs将为此对象绑定一个sqlite3_stmt实例,native层对象的指针
//保存在nStatement成员变量中
if(compileFlag) compileAndbindAllArgs();
}
~~~
来看compileAndbindAllArgs函数,其代码是:
**SQLiteProgram.java::compileAndbindAllArgs**
~~~
void compileAndbindAllArgs() {
......
//如果该对象还未和native层的sqlite3_stmt实例绑定,则调用compileSql函数
if(nStatement == 0) compileSql();
......//绑定参数
for(int index : mBindArgs.keySet()) {
Object value = mBindArgs.get(index);
if (value == null) {
native_bind_null(index);
}......//绑定其他类型的数据
}
}
~~~
compileSql函数将绑定Java层SQLiteQuery对象到一个Native的sqlite3_stmt实例。根据前文的分析,这个绑定是通过SQLiteCompileSql对象实施的,其相关代码如下:
**SQLiteProgram.java::compileSql**
~~~
private void compileSql() {
//如果mStatementType未设置STATEMENT_CACHEABLE标志,则每次都创建一个
// SQLiteCompiledSql对象。根据7.3.2节小标题3中的分析,该对象会真正和native层的
//sqlite_stmt实例绑定
if((mStatementType & STATEMENT_CACHEABLE) == 0) {
mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
nStatement = mCompiledSql.nStatement;
return;
}
//从SQLiteDatabase对象中查询是否已经缓存过符合该SQL语句要求的SQLiteCompiledSql
//对象
mCompiledSql = mDatabase.getCompiledStatementForSql(mSql);
if(mCompiledSql == null) {
//创建一个新的SQLiteCompiledSql对象,并把它保存到mDatabase中
mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
mCompiledSql.acquire();
mDatabase.addToCompiledQueries(mSql, mCompiledSql);
} ......
//保存Native层sqlite3_stmt实例指针到nStatement成员变量
nStatement= mCompiledSql.nStatement;
}
~~~
通过以上分析可以发现,SQLiteQuery将和一个代表SELECT命令的sqlite3_stmt实例绑定。同时,为了减少创建sqlite3_stmt实例的开销,SQLiteDatabase框架还会把对应的SQL语句和对应的SQLiteCompiledSql对象缓存起来。如果下次执行同样的SELECT语句,那么系统将直接取出之前保存的SQLiteCompiledSql对象,这样就不用重新创建sqlite3_stmt实例了。
* * * * *
**思考**:与直接使用SQLite API相比,SQLiteDatabase框架明显考虑了更多问题。不知读者自己封装C++ SQLite库时是否想到了这些问题?
* * * * *
(3) SQLiteCursor分析
再来看SQLiteCursor类,其构造函数的代码如下:
**SQLiteCursor.java::构造函数**
~~~
public SQLiteCursor(SQLiteCursorDriver driver,String editTable,
SQLiteQuery query) {
......
mDriver =driver;
mEditTable = editTable;
mColumnNameMap = null;
mQuery =query;//保存此SQLiteQuery对象
query.mDatabase.lock(query.mSql);
try {
//得到此次执行query得到的结果集所包含的列数
intcolumnCount = mQuery.columnCountLocked();
mColumns = new String[columnCount];
for(int i = 0; i < columnCount; i++) {
String columnName = mQuery.columnNameLocked(i);
//保存列名
mColumns[i] = columnName;
if ("_id".equals(columnName)) {
mRowIdColumnIndex = i;//保存“_id”列在结果集中的索引位置
}
}
}finally {
query.mDatabase.unlock();
}
}
~~~
SQLiteCursor比较简单,此处不再详述。
2. Cursor分析
至此,我们已经知道MediaProvider query返回的游标对象的真实类型了。现在,终于可以请Cursor家族登台亮相了,如图7-6所示。
:-: ![](http://img.blog.csdn.net/20150803130933944?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
图7-6 Cursor家族
图7-6中元素较多,包含的知识点也较为复杂,因此必须仔细阅读下文的解释。
通过7.3.1节中SQLiteTest示例可知,query查询(即SELECT命令)的结果和一个Native sqlite_stmt实例绑定在一起,开发者可通过遍历该sqlite_stmt实例得到自己想要的结果(例如,调用sqlite3_step遍历结果集中的行,然后通过sqlite3_column_xxx取出指定列的值)。查询结果集可通过数据库开发技术中一个专用术语——游标(Cursor)来遍历和获取。图7-6的左上部分是和Cursor有关的类,它们包括:接口类Cursor和CrossProcessCursor、抽象类AbstractCursor、AbstractWindowCursor,以及真正的实现类SQliteCursor。根据前面的分析,SQLiteCursor内部保存一个已经绑定了sqlite3_stmt实例的SQLiteQuery对象,故读者可简单地把SQLiteCursor看成是一个已经包含了查询结果集的游标对象,虽然此时还并未真正执行SQL语句。
如上所述,SQLiteCursor是一个已经包含了结果集的游标对象。从进程角度看,query的结果集目前还属于MediaProvider所在的进程,而本次query请求是由客户端发起的,所以一定要有一种方法将MediaProvider中的结果集传递到客户端进程。数据传递使用的技术很简单,就是大家耳熟能详的共享内存技术。SQLite API没有提供相关的功能,但是SQLiteDatabase框架对跨进程数据传递进行了封装,最终得到了图7-6左上部分的CursorWindow类。其代码中的注释明确表明了CursorWindow的作用:它是“A buffer containing multiplecursor rows”。
认识了CursorWindow,相信读者也能猜出query中数据传递的大致流程了。
MediaProvider将结果集中的数据存储到CursorWindow的共享内存中,然后客户端将其从共享内存中取出来即可。
上述流程描述是对的,但实际过程并非如此简单,因为SQLiteDatabase框架希望客户端看到的不是共享内存,而是一个代表结果集的游标对象,就好像客户端查询的是本进程中的数据库一样。由于存在这种要求[^①],Android构造了图7-6右下角的类家族。
其中,最重要的两个类是CursorToBulkCursorAdaptor和BulkCursorToCursorAdatpor。从名字上看,它们采用了设计模式中的Adaptor模式;从继承关系上看,这两个类将参与跨进程的Binder通信(其中客户端使用的BulkCursorToCursorAdaptor通过mBulkCursor与位于MediaProvider所在进程的CursorToBulkCursorAdaptor通信)。这两个类中最重要的是onMove函数,以后我们碰到时再作分析。
另外,图7-6中右上角部分展示了CursorWrapperInner类的派生关系。CursorWrapperInner类是ContentResolver query函数最终返回给客户端的游标对象的类型。CursorWrapperInner的目的应该是拓展CursorToBulkCursorAdaptor类的功能。
Cursor家族有些复杂。笔者觉得,目前对Cursor的架构设计有些过度(over-designed)。这不仅会导致我们分析时困难重重,并且也会对实际代码的运行效率造成一定损失。
下面我们将焦点放到跨进程的数据传输上。
[^①]:此处应该还存在其他方面的设计考虑,希望读者能参与讨论。
- 前言
- 第1章 搭建Android源码工作环境
- 1.1 Android系统架构
- 1.2 搭建开发环境
- 1.2.1 下载源码
- 1.2.2 编译源码
- 1.2.3 利用Eclipse调试system_process
- 1.3 本章小结
- 第2章 深入理解Java Binder和MessageQueue
- 2.1 概述
- 2.2 Java层中的Binder架构分析
- 2.2.1 Binder架构总览
- 2.2.2 初始化Java层Binder框架
- 2.2.3 addService实例分析
- 2.2.4 Java层Binder架构总结
- 2.3 心系两界的MessageQueue
- 2.3.1 MessageQueue的创建
- 2.3.2 提取消息
- 2.3.3 nativePollOnce函数分析
- 2.3.4 MessageQueue总结
- 2.4 本章小结
- 第3章 深入理解SystemServer
- 3.1 概述
- 3.2 SystemServer分析
- 3.2.1 main函数分析
- 3.2.2 Service群英会
- 3.3 EntropyService分析
- 3.4 DropBoxManagerService分析
- 3.4.1 DBMS构造函数分析
- 3.4.2 dropbox日志文件的添加
- 3.4.3 DBMS和settings数据库
- 3.5 DiskStatsService和DeviceStorageMonitorService分析
- 3.5.1 DiskStatsService分析
- 3.5.2 DeviceStorageManagerService分析
- 3.6 SamplingProfilerService分析
- 3.6.1 SamplingProfilerService构造函数分析
- 3.6.2 SamplingProfilerIntegration分析
- 3.7 ClipboardService分析
- 3.7.1 复制数据到剪贴板
- 3.7.2 从剪切板粘贴数据
- 3.7.3 CBS中的权限管理
- 3.8 本章小结
- 第4章 深入理解PackageManagerService
- 4.1 概述
- 4.2 初识PackageManagerService
- 4.3 PKMS的main函数分析
- 4.3.1 构造函数分析之前期准备工作
- 4.3.2 构造函数分析之扫描Package
- 4.3.3 构造函数分析之扫尾工作
- 4.3.4 PKMS构造函数总结
- 4.4 APK Installation分析
- 4.4.1 adb install分析
- 4.4.2 pm分析
- 4.4.3 installPackageWithVerification函数分析
- 4.4.4 APK 安装流程总结
- 4.4.5 Verification介绍
- 4.5 queryIntentActivities分析
- 4.5.1 Intent及IntentFilter介绍
- 4.5.2 Activity信息的管理
- 4.5.3 Intent 匹配查询分析
- 4.5.4 queryIntentActivities总结
- 4.6 installd及UserManager介绍
- 4.6.1 installd介绍
- 4.6.2 UserManager介绍
- 4.7 本章学习指导
- 4.8 本章小结
- 第5章 深入理解PowerManagerService
- 5.1 概述
- 5.2 初识PowerManagerService
- 5.2.1 PMS构造函数分析
- 5.2.2 init分析
- 5.2.3 systemReady分析
- 5.2.4 BootComplete处理
- 5.2.5 初识PowerManagerService总结
- 5.3 PMS WakeLock分析
- 5.3.1 WakeLock客户端分析
- 5.3.2 PMS acquireWakeLock分析
- 5.3.3 Power类及LightService类介绍
- 5.3.4 WakeLock总结
- 5.4 userActivity及Power按键处理分析
- 5.4.1 userActivity分析
- 5.4.2 Power按键处理分析
- 5.5 BatteryService及BatteryStatsService分析
- 5.5.1 BatteryService分析
- 5.5.2 BatteryStatsService分析
- 5.5.3 BatteryService及BatteryStatsService总结
- 5.6 本章学习指导
- 5.7 本章小结
- 第6章 深入理解ActivityManagerService
- 6.1 概述
- 6.2 初识ActivityManagerService
- 6.2.1 ActivityManagerService的main函数分析
- 6.2.2 AMS的 setSystemProcess分析
- 6.2.3 AMS的 installSystemProviders函数分析
- 6.2.4 AMS的 systemReady分析
- 6.2.5 初识ActivityManagerService总结
- 6.3 startActivity分析
- 6.3.1 从am说起
- 6.3.2 AMS的startActivityAndWait函数分析
- 6.3.3 startActivityLocked分析
- 6.4 Broadcast和BroadcastReceiver分析
- 6.4.1 registerReceiver流程分析
- 6.4.2 sendBroadcast流程分析
- 6.4.3 BROADCAST_INTENT_MSG消息处理函数
- 6.4.4 应用进程处理广播分析
- 6.4.5 广播处理总结
- 6.5 startService之按图索骥
- 6.5.1 Service知识介绍
- 6.5.2 startService流程图
- 6.6 AMS中的进程管理
- 6.6.1 Linux进程管理介绍
- 6.6.2 关于Android中的进程管理的介绍
- 6.6.3 AMS进程管理函数分析
- 6.6.4 AMS进程管理总结
- 6.7 App的 Crash处理
- 6.7.1 应用进程的Crash处理
- 6.7.2 AMS的handleApplicationCrash分析
- 6.7.3 AppDeathRecipient binderDied分析
- 6.7.4 App的Crash处理总结
- 6.8 本章学习指导
- 6.9 本章小结
- 第7章 深入理解ContentProvider
- 7.1 概述
- 7.2 MediaProvider的启动及创建
- 7.2.1 Context的getContentResolver函数分析
- 7.2.2 MediaStore.Image.Media的query函数分析
- 7.2.3 MediaProvider的启动及创建总结
- 7.3 SQLite创建数据库分析
- 7.3.1 SQLite及SQLiteDatabase家族
- 7.3.2 MediaProvider创建数据库分析
- 7.3.3 SQLiteDatabase创建数据库的分析总结
- 7.4 Cursor 的query函数的实现分析
- 7.4.1 提取query关键点
- 7.4.2 MediaProvider 的query分析
- 7.4.3 query关键点分析
- 7.4.4 Cursor query实现分析总结
- 7.5 Cursor close函数实现分析
- 7.5.1 客户端close的分析
- 7.5.2 服务端close的分析
- 7.5.3 finalize函数分析
- 7.5.4 Cursor close函数总结
- 7.6 ContentResolver openAssetFileDescriptor函数分析
- 7.6.1 openAssetFileDescriptor之客户端调用分析
- 7.6.2 ContentProvider的 openTypedAssetFile函数分析
- 7.6.3 跨进程传递文件描述符的探讨
- 7.6.4 openAssetFileDescriptor函数分析总结
- 7.7 本章学习指导
- 7.8 本章小结
- 第8章 深入理解ContentService和AccountManagerService
- 8.1 概述
- 8.2 数据更新通知机制分析
- 8.2.1 初识ContentService
- 8.2.2 ContentResovler 的registerContentObserver分析
- 8.2.3 ContentResolver的 notifyChange分析
- 8.2.4 数据更新通知机制总结和深入探讨
- 8.3 AccountManagerService分析
- 8.3.1 初识AccountManagerService
- 8.3.2 AccountManager addAccount分析
- 8.3.3 AccountManagerService的分析总结
- 8.4 数据同步管理SyncManager分析
- 8.4.1 初识SyncManager
- 8.4.2 ContentResolver 的requestSync分析
- 8.4.3 数据同步管理SyncManager分析总结
- 8.5 本章学习指导
- 8.6 本章小结