Android源码目录packages\providers下的应用是下载,通话等内置基本应用提供数据存储和操作的provider应用,本文章将针对ContactsProvider源码的架构和实现展开分析。(注:本文使用使用android4.0版本进行分析)

1、架构设计

ContactsProvider中数据操作基类是AbstractContactsProvider.java(参见frameworks\ex\common\java\com\android\common\content\SQLiteContentProvider.java),它继承ContentProvider.java实现SQLiteTransactionListener.java,类结构如下图所示:

图 1 provider结构图

该类是抽象基类,在里面实现了父类的insert、delete和update三个抽象方法,在这三个方法中在其中使用了事务对数据库进行操作。该类设计时,使用了模板模式模板方法为insertInTransaction,updateInTransaction和deleteInTransaction。该类在对数据库进行事务操作的同时,对子类开放了onRollback,onCommit等事物回调方法,子类可以根据自己的业务特点进行扩展。增删改查的调用过程如下图2 3 4所示:

图2  插入操作

图 3 更新操作

图 4 删除操作

在整个设计中有两个类继承了AbstractContactsProvider.java,一个是ContactsProvider2.java,我们在调用系统的联系人数据时基本都是调用该类,里面封装了所有联系人的数据操作。第二个是ProfileProvider.java,该类是ContactsProvider2.java的委托类。这三个类的类间关系如下图5所示:

图 5 类间关系图

类ContactsTransaction.java是对事务的管理类,主要对进行的事务进行管理,类似一个事务池。是AbstractContactsProvider.java中事务处理的核心类。

DataRowHandler.java是数据处理抽象类,实现了对数据的增删改操作,子类有如下几个:

[java] view plaincopy
  1. DataRowHandlerForCommonDataKind.java
  2. DataRowHandlerForCustomMimetype.java
  3. DataRowHandlerForEmail.java
  4. DataRowHandlerForGroupMembership.java
  5. DataRowHandlerForIm.java
  6. DataRowHandlerForNickname.java
  7. DataRowHandlerForNote.java
  8. DataRowHandlerForOrganization.java
  9. DataRowHandlerForPhoneNumber.java
  10. DataRowHandlerForPhoto.java
  11. DataRowHandlerForStructuredName.java
  12. DataRowHandlerForStructuredPostal.java

这些子类在ContactsProvider2#initDataRowHandlers中初始化

[java] view plaincopy
  1. private void initDataRowHandlers(Map<String, DataRowHandler> handlerMap,
  2. ContactsDatabaseHelper dbHelper, ContactAggregator contactAggregator,
  3. PhotoStore photoStore) {
  4. Context context = getContext();
  5. handlerMap.put(Email.CONTENT_ITEM_TYPE,
  6. new DataRowHandlerForEmail(context, dbHelper, contactAggregator));
  7. handlerMap.put(Im.CONTENT_ITEM_TYPE,
  8. new DataRowHandlerForIm(context, dbHelper, contactAggregator));
  9. handlerMap.put(Organization.CONTENT_ITEM_TYPE,
  10. new DataRowHandlerForOrganization(context, dbHelper, contactAggregator));
  11. handlerMap.put(Phone.CONTENT_ITEM_TYPE,
  12. new DataRowHandlerForPhoneNumber(context, dbHelper, contactAggregator));
  13. handlerMap.put(Nickname.CONTENT_ITEM_TYPE,
  14. new DataRowHandlerForNickname(context, dbHelper, contactAggregator));
  15. handlerMap.put(StructuredName.CONTENT_ITEM_TYPE,
  16. new DataRowHandlerForStructuredName(context, dbHelper, contactAggregator,
  17. mNameSplitter, mNameLookupBuilder));
  18. handlerMap.put(StructuredPostal.CONTENT_ITEM_TYPE,
  19. new DataRowHandlerForStructuredPostal(context, dbHelper, contactAggregator,
  20. mPostalSplitter));
  21. handlerMap.put(GroupMembership.CONTENT_ITEM_TYPE,
  22. new DataRowHandlerForGroupMembership(context, dbHelper, contactAggregator,
  23. mGroupIdCache));
  24. handlerMap.put(Photo.CONTENT_ITEM_TYPE,
  25. new DataRowHandlerForPhoto(context, dbHelper, contactAggregator, photoStore));
  26. handlerMap.put(Note.CONTENT_ITEM_TYPE,
  27. new DataRowHandlerForNote(context, dbHelper, contactAggregator));
  28. }

这些子类在getDataRowHandler方法中中通过mimetype进行调用:

[java] view plaincopy
  1. public DataRowHandler getDataRowHandler(final String mimeType) {
  2. if (inProfileMode()) {
  3. return getDataRowHandlerForProfile(mimeType);
  4. }
  5. DataRowHandler handler = mDataRowHandlers.get(mimeType);
  6. if (handler == null) {
  7. handler = new DataRowHandlerForCustomMimetype(
  8. getContext(), mContactsHelper, mContactAggregator, mimeType);
  9. mDataRowHandlers.put(mimeType, handler);
  10. }
  11. return handler;
  12. }

getDataRowHandler方法在insertData,deleteData和updateData方法中被调用。

[java] view plaincopy
  1. /**
  2. * Inserts an item in the data table
  3. *
  4. * @param values the values for the new row
  5. * @return the row ID of the newly created row
  6. */
  7. private long insertData(ContentValues values, boolean callerIsSyncAdapter) {
  8. long id = 0;
  9. mValues.clear();
  10. mValues.putAll(values);
  11. long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);
  12. // Replace package with internal mapping
  13. final String packageName = mValues.getAsString(Data.RES_PACKAGE);
  14. if (packageName != null) {
  15. mValues.put(DataColumns.PACKAGE_ID, mDbHelper.get().getPackageId(packageName));
  16. }
  17. mValues.remove(Data.RES_PACKAGE);
  18. // Replace mimetype with internal mapping
  19. final String mimeType = mValues.getAsString(Data.MIMETYPE);
  20. if (TextUtils.isEmpty(mimeType)) {
  21. throw new IllegalArgumentException(Data.MIMETYPE + " is required");
  22. }
  23. mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.get().getMimeTypeId(mimeType));
  24. mValues.remove(Data.MIMETYPE);
  25. DataRowHandler rowHandler = <span style="color: rgb(102, 102, 204);">getDataRowHandler(mimeType)</span>;
  26. id = rowHandler.insert(mActiveDb.get(), mTransactionContext.get(), rawContactId, mValues);
  27. if (!callerIsSyncAdapter) {
  28. mTransactionContext.get().markRawContactDirty(rawContactId);
  29. }
  30. mTransactionContext.get().rawContactUpdated(rawContactId);
  31. return id;
  32. }

所有数据的mimetype都被存储在表Tables.MIMETYPES中,该只有两个字段_id和mimetype。

[java] view plaincopy
  1. db.execSQL("CREATE TABLE " + Tables.MIMETYPES + " ("
  2. + MimetypesColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
  3. + MimetypesColumns.MIMETYPE + " TEXT NOT NULL" + ");");

Tables.MIMETYPES表数据的存储是在ContactsDatabaseHelper#lookupAndCacheId中进行的,具体调用过程如下图 6 所示:

图 6 mimetype数据调用流程图

2、表结构

ContactsProvider 中共创建了25张表,

[java] view plaincopy
  1. public static final String CONTACTS = "contacts";
  2. public static final String RAW_CONTACTS = "raw_contacts";
  3. public static final String STREAM_ITEMS = "stream_items";
  4. public static final String STREAM_ITEM_PHOTOS = "stream_item_photos";
  5. public static final String PHOTO_FILES = "photo_files";
  6. public static final String PACKAGES = "packages";
  7. public static final String MIMETYPES = "mimetypes";
  8. public static final String PHONE_LOOKUP = "phone_lookup";
  9. public static final String NAME_LOOKUP = "name_lookup";
  10. public static final String AGGREGATION_EXCEPTIONS = "agg_exceptions";
  11. public static final String SETTINGS = "settings";
  12. public static final String DATA = "data";
  13. public static final String GROUPS = "groups";
  14. public static final String PRESENCE = "presence";
  15. public static final String AGGREGATED_PRESENCE = "agg_presence";
  16. public static final String NICKNAME_LOOKUP = "nickname_lookup";
  17. public static final String CALLS = "calls";
  18. public static final String STATUS_UPDATES = "status_updates";
  19. public static final String PROPERTIES = "properties";
  20. public static final String ACCOUNTS = "accounts";
  21. public static final String VISIBLE_CONTACTS = "visible_contacts";
  22. public static final String DIRECTORIES = "directories";
  23. public static final String DEFAULT_DIRECTORY = "default_directory";
  24. public static final String SEARCH_INDEX = "search_index";
  25. public static final String VOICEMAIL_STATUS = "voicemail_status";

这些表数据对开发者开放的api在 \frameworks\base\core\java\android\provider\文件夹下,

[java] view plaincopy
  1. CallLog.java
  2. Contacts.java
  3. ContactsContract.java
  4. SocialContract.java
  5. SyncConstValue.java
  6. VoicemailContract.java

由于数据查询涉及多个表间关系,大量数据的查询都是通过视图来完成的,创建了如下8个视图,而这几个视图是大家在调用api进行查询时显示的数据:

[java] view plaincopy
  1. public interface Views {
  2. public static final String DATA = "view_data";
  3. public static final String RAW_CONTACTS = "view_raw_contacts";
  4. public static final String CONTACTS = "view_contacts";
  5. public static final String ENTITIES = "view_entities";
  6. public static final String RAW_ENTITIES = "view_raw_entities";
  7. public static final String GROUPS = "view_groups";
  8. public static final String DATA_USAGE_STAT = "view_data_usage_stat";
  9. public static final String STREAM_ITEMS = "view_stream_items";
  10. }

4、总结

ContactsProvder是provider源码中数据处理和架构比较全面的一个应用,可以将它的架构核心抽离出来供大家参考和借鉴,以下类构成了整个架构的核心类,可以重点研究:

[java] view plaincopy
  1. AbstractContactsProvider.java
  2. ContactAggregator.java
  3. ContactsDatabaseHelper.java

Android ContactsProvider源码分析相关推荐

  1. Android HandlerThread 源码分析

    HandlerThread 简介: 我们知道Thread线程是一次性消费品,当Thread线程执行完一个耗时的任务之后,线程就会被自动销毁了.如果此时我们又有一 个耗时任务需要执行,我们不得不重新创建 ...

  2. Android ADB 源码分析(三)

    前言 之前分析的两篇文章 Android Adb 源码分析(一) 嵌入式Linux:Android root破解原理(二) 写完之后,都没有写到相关的实现代码,这篇文章写下ADB的通信流程的一些细节 ...

  3. 【Android SDM660源码分析】- 02 - UEFI XBL QcomChargerApp充电流程代码分析

    [Android SDM660源码分析]- 02 - UEFI XBL QcomChargerApp充电流程代码分析 一.加载 UEFI 默认应用程序 1.1 LaunchDefaultBDSApps ...

  4. 【Android SDM660源码分析】- 03 - UEFI XBL GraphicsOutput BMP图片显示流程

    [Android SDM660源码分析]- 03 - UEFI XBL GraphicsOutput BMP图片显示流程 1. GraphicsOutput.h 2. 显示驱动初化 DisplayDx ...

  5. 【Android SDM660源码分析】- 01 - 如何创建 UEFI XBL Protocol DXE_DRIVER 驱动及UEFI_APPLICATION 应用程序

    [Android SDM660源码分析]- 01 - 如何创建 UEFI XBL Protocol DXE_DRIVER 驱动及UEFI_APPLICATION 应用程序 一.创建DXE_DRIVER ...

  6. 【Android SDM660源码分析】- 04 - UEFI ABL LinuxLoader 代码分析

    [Android SDM660源码分析]- 04 - UEFI ABL LinuxLoader 代码分析 1. LinuxLoader.c 系列文章: <[Android SDM660开机流程] ...

  7. Android 音频源码分析——AndroidRecord录音(一)

    Android 音频源码分析--AndroidRecord录音(一) Android 音频源码分析--AndroidRecord录音(二) Android 音频源码分析--AndroidRecord音 ...

  8. Android框架源码分析——从设计模式角度看 Retrofit 核心源码

    Android框架源码分析--从设计模式角度看 Retrofit 核心源码 Retrofit中用到了许多常见的设计模式:代理模式.外观模式.构建者模式等.我们将从这三种设计模式入手,分析 Retrof ...

  9. 人人网官方Android客户端源码分析(1)

    ContentProvider是不同应用程序之间进行数据交换的标准API,ContentProvider以某种Uri的形式对外提供数据,允许其他应用访问或修改数据;其他应用程序使用ContentRes ...

  10. Android 音频源码分析——AudioTrack设备选择

    Android 音频源码分析--AndroidRecord录音(一) Android 音频源码分析--AndroidRecord录音(二) Android 音频源码分析--AndroidRecord音 ...

最新文章

  1. TortoiseSVN文件夹图标不显示
  2. Cocos2d-x Eclipse下程序运行产生错误Effect initCheck() returned -1
  3. Git config 查看和设置配置信息
  4. python 数据分析学什么-python数据分析哪些课程好?
  5. php 企业号文本消息推送,Python如何实现微信企业号文本消息推送功能的示例
  6. 学习笔记Kafka(七)—— Kafka 与Spark集成 —— 原理介绍与开发环境配置、实战
  7. eclipse中配置Maven仓库
  8. php warning: file_get_contents,Ecshop报警告:Warning: file_get_contents
  9. 递归->记忆化搜索->严格表
  10. vmware6.5.2序列号_教你如何查询苹果序列号,查询是否为官换机、激活时间等
  11. 利用工厂模式实现怪物系统
  12. JavaScript笔记-使用反引号格式化字符串
  13. 视频移动侦测VMD的实现
  14. 内核并发控制---互斥量(来自网易)
  15. 标准 C I/O函数
  16. 大数据将会带来什么机遇
  17. html回到上一步,【答疑】Photoshop返回上一步和下一步快捷键是什么? - 视频教程线上学...
  18. 微信公众平台之模拟登录
  19. poj 1755 Triathlon (半平面交解一元二次不等式)(切割求半平面交)
  20. iOS 关于sim卡

热门文章

  1. Android USB Camera(2) : UVC协议分析
  2. 2022年高处安装、维护、拆除理论题库及模拟考试
  3. sqluldr2用法
  4. ppt flash无法播放解决方法
  5. 欧华android导航刷机,寻找欧华DVD导航一体机刷机文件。
  6. 板翅式换热器翅片表面传热与阻力特性性能分析
  7. C++实现 模糊综合评价法
  8. 解决keep-live使用之后的问题
  9. 罗技GHub驱动长时间加载/初始化【完美解决】
  10. NC65销售订单功能导入