在MediaProvider中触发数据库的是attach函数,其代码如下:
**MediaProvider::attach**
~~~
private Uri attachVolume(String volume) {
Contextcontext = getContext();
DatabaseHelper db;
if(INTERNAL_VOLUME.equals(volume)) {
......//针对内部存储空间的数据库
} elseif (EXTERNAL_VOLUME.equals(volume)) {
......
String dbName = "external-" +Integer.toHexString(volumeID) + ".db";
//①构造一个DatabaseHelper对象
db =new DatabaseHelper(context, dbName, false,
false, mObjectRemovedCallback);
......//省略不相关的内容
}......
if(!db.mInternal) {
//②调用DatabaseHelper的getWritableDatabase函数,该函数返回值的类型为
//SQLiteDatabase,即代表SQLite数据库的对象
createDefaultFolders(db.getWritableDatabase());
.......
}
......
}
~~~
以上代码中列出了两个关键点,分别是:
- 构造一个DatabaseHelper对象。
- 调用DatabaseHelper对象的getWritableDatabase函数得到一个代表SQLite数据库的SQLiteDatabase对象。
1. DatabaseHelper分析
DatabaseHelper是MediaProvider的内部类,它从SQLiteOpenHelper派生,其构造函数的代码如下:
(1) DatabaseHelper构造函数分析
**MediaProvider.java::DatabaseHelper**
~~~
public DatabaseHelper(Context context, Stringname, boolean internal,
boolean earlyUpgrade,
SQLiteDatabase.CustomFunction objectRemovedCallback) {
//重点关注其基类的构造函数
super(context, name, null,DATABASE_VERSION);
mContext = context;
mName= name;
mInternal = internal;
mEarlyUpgrade = earlyUpgrade;
mObjectRemovedCallback = objectRemovedCallback;
}
~~~
SQLiteOpenHelper作为DatabaseHelper的基类,其构造函数的代码如下:
**SQLiteOpenHelper.java::SQLiteOpenHelper**
~~~
public SQLiteOpenHelper(Context context, Stringname, CursorFactory factory,
int version) {
//调用另外一个构造函数,注意它新建了一个默认的错误处理对象
this(context, name, factory, version, newDefaultDatabaseErrorHandler());
}
public SQLiteOpenHelper(Context context, Stringname, CursorFactory factory,
int version, DatabaseErrorHandlererrorHandler) {
......
mContext= context;
mName =name;
//看到”factory“一词,读者要能想到设计模式中的工厂模式,在本例中该变量为null
mFactory= factory;
mNewVersion = version;
mErrorHandler= errorHandler;
}
~~~
上面这些函数都比较简单,其中却蕴含一个较为深刻的设计理念,具体如下:
从SQLiteOpenHelper的构造函数中可知,MediaProvider对应的数据库对象(即SQLiteDatabase实例)并不在该函数中创建。那么,代表数据库的SQLiteDatabase实例是何时创建呢?
此处使用了所谓的延迟创建(lazy creation)的方法,即SQLiteDatabase实例真正创建的时机是在第一次使用它的时候,也就是本例中第二个关键点函数getWritableDatabase。
在分析getWritableDatabase函数之前,先介绍一些和延迟创建相关的知识。
延迟创建或延迟初始化(lazy intializtion)所谓的“重型”资源(如占内存较大或创建时间比较长的资源),是系统开发和设计中常用的一种策略[^①]。在使用这种策略时,开发人员不仅在资源创建时“斤斤计较”,在资源释放的问题上也是“慎之又。资源释放的控制一般会采用引用计数技术。
结合前面对SQLiteDatabase的介绍会发现,SQLiteDatabase这个框架,在设计时不是简单地将SQLite API映射到Java层,而是有大量更为细致的考虑。例如,在这个框架中,资源创建采用了lazy creation方法,资源释放又利用SQLiteClosable来控制生命周期。
* * * * *
**建议**:此框架虽更完善、更具扩展性,但是使用它比直接使用SQLite API要复杂得多,因此在开发过程中,应当根据实际情况综合考虑是否使用该框架。例如,笔者在开发公司的DLNA解决方案时,就直接使用了SQLiteAPI,而没使用这个框架。
* * * * *
(2) getWritableDatabase函数分析
现在来看getWritableDatabase的代码。
**SQLiteDatabase.java::getWritableDatabase**
~~~
public synchronized SQLiteDatabasegetWritableDatabase() {
if(mDatabase != null) {
//第一次调用该函数时mDatabase还未创建。以后的调用将直接返回已经创建好的mDatabase
}
booleansuccess = false;
SQLiteDatabase db = null;
if (mDatabase != null) mDatabase.lock();
try {
mIsInitializing = true;
if(mName == null) {
db = SQLiteDatabase.create(null);
}else {
//①调用Context的openOrCreateDatabase创建数据库
db = mContext.openOrCreateDatabase(mName, 0,
mFactory, mErrorHandler);
}
intversion = db.getVersion();
if(version != mNewVersion) {
db.beginTransaction();
try {
if (version == 0) {
/*
如果初次创建该数据库(即对应的数据库文件不存在),则调用子类实现的
onCreate函数。子类实现的onCreate函数将完成数据库建表等操作。读者不妨
查看MediaProviderDatabaseHelper实现的onCreate函数
*/
onCreate(db);
} else {
//如果从数据库文件中读出来的版本号与MediaProvider设置的版本号不一致,
//则调用子类实现的onDowngrade或onUpgrade做相应处理
if (version > mNewVersion)
onDowngrade(db, version,mNewVersion);
else
onUpgrade(db, version,mNewVersion);
}
db.setVersion(mNewVersion);
db.setTransactionSuccessful();
}finally {
db.endTransaction();
}
}// if (version != mNewVersion)判断结束
onOpen(db);//调用子类实现的onOpen函数
success =true;
return db;
}......
~~~
由以上代码可知,代表数据库的SQLiteDatabase对象是由context openOrCreateDatabase创建的。下面单起一节具体分析此函数。
2. ContextImpl openOrCreateDatabase分析
(1) openOrCreateDatabase函数分析
相信读者已能准确定位openOrCreateDatabase函数的真正实现了,它就在ContextImpl.java中,其代码如下:
**ContextImpl.java::openOrCreateDatabase**
~~~
public SQLiteDatabase openOrCreateDatabase(Stringname, int mode,
CursorFactory factory,DatabaseErrorHandler errorHandler) {
File f =validateFilePath(name, true);
//调用SQLiteDatabase的静态函数openOrCreateDatabase创建数据库
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(f.getPath(),
factory, errorHandler);
setFilePermissionsFromMode(f.getPath(), mode, 0);
return db;
}
~~~
**SQLiteDatabase.java::openDatabase**
~~~
public static SQLiteDatabase openDatabase(Stringpath, CursorFactory
factory, int flags,DatabaseErrorHandlererrorHandler) {
//又调用openDatabase创建SQLiteDatabase实例,真的是层层转包啊
SQLiteDatabase sqliteDatabase = openDatabase(path, factory,
flags, errorHandler,(short) 0);
if(sBlockSize == 0) sBlockSize = newStatFs("/data").getBlockSize();
//为该SQLiteDatabase实例设置一些参数。这些内容和SQLite本身的特性有关,本书不
//拟深入讨论这方面的内容,感兴趣的读者不妨参考SQLite官网提供的资料
sqliteDatabase.setPageSize(sBlockSize);
sqliteDatabase.setJournalMode(path, "TRUNCATE");
synchronized(mActiveDatabases) {
mActiveDatabases.add(
newWeakReference<SQLiteDatabase>(sqliteDatabase));
}
returnsqliteDatabase;
}
~~~
openDatabase将真正创建一个SQLiteDatabase实例,其相关代码是:
**SqliteDatabase.java::openDatabase**
~~~
private static SQLiteDatabase openDatabase(Stringpath, CursorFactory factory,
int flags,DatabaseErrorHandler errorHandler,
shortconnectionNum) {
//构造一个SQLiteDatabase实例
SQLiteDatabase db = new SQLiteDatabase(path, factory, flags,errorHandler,
connectionNum);
try {
db.dbopen(path, flags);//打开数据库,dbopen是一个native函数
db.setLocale(Locale.getDefault());//设置Locale
......
returndb;
}......
}
~~~
其实openDatabase主要就干了两件事情,即创建一个SQLiteDatabase实例,然后调用该实例的dbopen函数。
(2) SQLiteDatabase的构造函数及dbopen函数分析
先看SQLitedDatabase的构造函数,代码如下:
**SQLitedDatabase.java::SQLiteDatabas**e
~~~
private SQLiteDatabase(String path, CursorFactoryfactory, int flags,
DatabaseErrorHandler errorHandler, short connectionNum) {
setMaxSqlCacheSize(DEFAULT_SQL_CACHE_SIZE);
mFlags =flags;
mPath = path;
mFactory= factory;
mPrograms= new WeakHashMap<SQLiteClosable,Object>();
// config_cursorWindowSize值为2048,所以下面得到的limit值应该为8MB
int limit= Resources.getSystem().getInteger(
com.android.internal.R.integer.config_cursorWindowSize)
* 1024 * 4;
native_setSqliteSoftHeapLimit(limit);
}
~~~
前面说过,Java层的SQLiteDatabase对象会和一个Native层sqlite3实例绑定,从以上代码中可发现,绑定的工作并未在构造函数中开展。实际上,该工作是由dbopen函数完成的,其相关代码如下:
**android_database_SQLiteDatabase.cpp::dbopen**
~~~
static void dbopen(JNIEnv* env, jobject object,jstring pathString, jint flags)
{
int err;
sqlite3* handle = NULL;
sqlite3_stmt * statement = NULL;
charconst * path8 = env->GetStringUTFChars(pathString, NULL);
intsqliteFlags;
registerLoggingFunc(path8);
if(flags & CREATE_IF_NECESSARY) {
sqliteFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
} ......
//调用sqlite3_open_v2函数创建数据库,sqlite3_open_v2和示例中的sqlite3_open类似
//handle用于存储新创建的sqlite3*类型的实例
err =sqlite3_open_v2(path8, &handle, sqliteFlags, NULL);
......
sqlite3_soft_heap_limit(sSqliteSoftHeapLimit);
err =sqlite3_busy_timeout(handle, 1000 /* ms */);
......
//Android在原生SQLite之上还做了一些特殊的定制,相关内容留待本节最后分析
err =register_android_functions(handle, UTF16_STORAGE);
//将handle保存到Java层的SQLiteDatabase对象中,这样Java层SQLiteDatabase实例
//就和一个Native层的sqlite3实例绑定到一起了
env->SetIntField(object, offset_db_handle, (int) handle);
handle =NULL; // The caller owns the handle now.
done:
if(path8 != NULL) env->ReleaseStringUTFChars(pathString, path8);
if(statement != NULL) sqlite3_finalize(statement);
if(handle != NULL) sqlite3_close(handle);
}
~~~
从上述代码可知,使用dbopen函数其实就是为了得到Native层的一个sqlite3实例。另外,Android对SQLite还设置了一些平台相关的函数,这部分内容将在后文进行分析。
3. SQLiteCompiledSql介绍
前文曾提到,Native层sqlite3_stmt实例的封装是由未对开发者公开的类SQLiteCompileSql完成的。由于它的隐秘性,没有在图7-4中把它列出来。现在我们就来揭开它神秘的面纱,其代码如下:
**SQLiteCompliteSql.java::SQLiteCompiledSql**
~~~
SQLiteCompiledSql(SQLiteDatabase db, String sql) {
db.verifyDbIsOpen();
db.verifyLockOwner();
mDatabase = db;
mSqlStmt= sql;
......
nHandle= db.mNativeHandle;
native_compile(sql);//调用native_compile函数,代码如下
}
~~~
**android_database_SQLiteCompliteSql.cpp::native_compile**
~~~
static void native_compile(JNIEnv* env, jobjectobject, jstring sqlString)
{
compile(env, object, GET_HANDLE(env, object), sqlString);
}
//来看compile的实现
sqlite3_stmt * compile(JNIEnv* env, jobjectobject,
sqlite3 * handle,jstring sqlString)
{
int err;
jcharconst * sql;
jsizesqlLen;
sqlite3_stmt * statement = GET_STATEMENT(env, object);
if(statement != NULL) ....//释放之前的sqlite3_stmt实例
sql = env->GetStringChars(sqlString,NULL);
sqlLen =env->GetStringLength(sqlString);
//调用sqlite3_prepare16_v2得到一个sqlite3_stmt实例
err =sqlite3_prepare16_v2(handle, sql, sqlLen * 2, &statement, NULL);
env->ReleaseStringChars(sqlString, sql);
if (err== SQLITE_OK) {
//保存到Java层的SQLiteCompliteSql对象中
env->SetIntField(object, gStatementField, (int)statement);
return statement;
} ......
}
~~~
当compile函数执行完后,一个绑定了SQL语句的sqlite3_stmt实例就和Java层的SQLiteCompileSql对象绑定到一起了。
4. Android SQLite自定义函数介绍
本节将介绍Android在SQLite上自定义的一些函数。一切还得从SQL的触发器说起。
(1) 触发器介绍
触发器(Trigger)是数据库开发技术中一个常见的术语。其本质非常简单,就是在指定表上发生特定事情时,数据库需要执行的某些操作。还是有点模糊吧?再来看MediaProvider设置的一个触发器:
~~~
db.execSQL("CREATE TRIGGER IF NOT EXISTSimages_cleanup DELETE ON images " +
"BEGIN " +
"DELETE FROM thumbnails WHERE image_id = old._id;" +
"SELECT _DELETE_FILE(old._data);" +
"END");
~~~
上面这条SQL语句是什么意思呢?
- CREATE TRIGGER IF NOT EXITSimages_cleanup:如果没有定义名为images_cleanup的触发器,就创建一个名为images_cleanup的触发器。
- DELETE ON images:设置该触发器的触发条件。显然,当我们对images表执行delete操作时,该触发器将被触发。
BEGIN和END之间则定义了该触发器要执行的动作。从前面的代码可知,它将执行两项操作:
- 删除thumbnails(缩略图)表中对应的信息。为什么要删除缩略图呢?因为原图的信息已经不存在了,留着它没用。
- 执行_DELETE_FILE函数,其参数是old.data。从名字上来看,这个函数的功能应为删除文件。为什么要删除此文件?原因也很简单,数据库都没有该项信息了,还留着图片干什么!另外,如不删除文件,下一次媒体扫描时就又会把它们找到。
* * * * *
**提示**:_DELETE_FILE这个操作曾给笔者及同仁带来极大困扰,因为最开始并不知道有这个触发器。结果好不容易下载的测试文件全部被删除了。另外,由于MediaProvider本身的设计缺陷,频繁挂/卸载SD卡时也会错误删除数据库信息(这个缺陷只能尽量避免,无法彻底根除),结果实体文件也被删除掉了。
* * * * *
有人可能会感到奇怪,这个_DELETE_FILE函数是谁设置的呢?答案就在前面提到的register_android_functions中。
(2) register_android_functions介绍
register_android_functions在dbopen中被调用,其代码如下:
**sqlite3_android.cpp::register_android_functions**
~~~
//dbopen调用它时,第二个参数设置为0
extern "C" intregister_android_functions(sqlite3 * handle, int utf16Storage)
{
int err;
UErrorCode status = U_ZERO_ERROR;
UCollator * collator = ucol_open(NULL, &status);
......
if(utf16Storage) {
err =sqlite3_exec(handle, "PRAGMA encoding = 'UTF-16'", 0, 0, 0);
......
} else {
//sqlite3_create_collation_xx定义一个用于排序的文本比较函数,读者可自行阅读
//SQLite官方文档以获得更详细的说明
err = sqlite3_create_collation_v2(handle,"UNICODE",
SQLITE_UTF8, collator, collate8,
(void(*)(void*))localized_collator_dtor);
}
/*
调用sqlite3_create_function创建一个名为"PHONE_NUMBERS_EQUAL"的函数,
第三个参数2表示该函数有两个参数,SQLITE_UTF8表示字符串编码为UTF8,
phone_numbers_equal为该函数对应的函数指针,也就是真正会执行的函数。注意
"PHONE_NUMBERS_EQUAL"是SQL语句中使用的函数名,phone_numbers_equal是Native
层对应的函数
*/
err =sqlite3_create_function(
handle, "PHONE_NUMBERS_EQUAL", 2,
SQLITE_UTF8, NULL, phone_numbers_equal, NULL, NULL);
......
//注册_DELETE_FILE对应的函数为delete_file
err =sqlite3_create_function(handle, "_DELETE_FILE", 1, SQLITE_UTF8,
NULL, delete_file, NULL, NULL);
if (err!= SQLITE_OK) {
return err;
}
#if ENABLE_ANDROID_LOG
err =sqlite3_create_function(handle, "_LOG", 1, SQLITE_UTF8,
NULL, android_log,NULL, NULL);
......
#endif
......//和PHONE相关的一些函数
returnSQLITE_OK;
}
~~~
register_android_functions注册了Android平台上定制的一些函数。来看和_DELETE_FILE有关的delete_file函数,其代码为:
**Sqlite3_android.cpp::delete_file**
~~~
static void delete_file(sqlite3_context * context,int argc,
sqlite3_value** argv)
{
if (argc!= 1) {
sqlite3_result_int(context, 0);
return;
}
//从argv中取出第一个参数,这个参数是触发器调用_DELETE_FILE时传递的
charconst * path = (char const *)sqlite3_value_text(argv[0]);
......
/*
Android4.0之后,系统支持多个存储空间(很多平板都有一块很大的内部存储空间)。
为了保持兼容性,环境变量EXTERNAL_STORAGE还是指向sd卡的挂载目录,而其他存储设备的
挂载目录由SECCONDARY_STORAGE表示,各个挂载目录由冒号分隔。
下面这段代码用于判断_DELETE_FILE函数所传递的文件路径是不是正确的
*/
boolgood_path = false;
charconst * external_storage = getenv("EXTERNAL_STORAGE");
if(external_storage && strncmp(external_storage,
path,strlen(external_storage)) == 0) {
good_path = true;
} else {
charconst * secondary_paths = getenv("SECONDARY_STORAGE");
while (secondary_paths && secondary_paths[0]) {
const char* colon = strchr(secondary_paths, ':');
int length = (colon ? colon - secondary_paths :
strlen(secondary_paths));
if (strncmp(secondary_paths, path, length) == 0) {
good_path = true;
}
secondary_paths += length;
while (*secondary_paths == ':')secondary_paths++;
}
}
if(!good_path) {
sqlite3_result_null(context);
return;
}
//调用unlink删除文件
int err= unlink(path);
if (err!= -1) {
sqlite3_result_int(context, 1);//设置返回值
} else {
sqlite3_result_int(context, 0);
}
}
~~~
[^①]:其实这是一种广义的设计模式,读者可参考《Pattern-Oriented Software Architecture Volume 3: Patterns forResource Management》一书以加深理解。
- 前言
- 第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 本章小结