Android ContactsProvider源码分析

article/2025/10/20 13:00:38

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 plain copy
  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 plain copy
  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 plain copy
  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 plain copy
  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.   
  12.        long rawContactId = mValues.getAsLong(Data.RAW_CONTACT_ID);  
  13.   
  14.        // Replace package with internal mapping  
  15.        final String packageName = mValues.getAsString(Data.RES_PACKAGE);  
  16.        if (packageName != null) {  
  17.            mValues.put(DataColumns.PACKAGE_ID, mDbHelper.get().getPackageId(packageName));  
  18.        }  
  19.        mValues.remove(Data.RES_PACKAGE);  
  20.   
  21.        // Replace mimetype with internal mapping  
  22.        final String mimeType = mValues.getAsString(Data.MIMETYPE);  
  23.        if (TextUtils.isEmpty(mimeType)) {  
  24.            throw new IllegalArgumentException(Data.MIMETYPE + " is required");  
  25.        }  
  26.   
  27.        mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.get().getMimeTypeId(mimeType));  
  28.        mValues.remove(Data.MIMETYPE);  
  29.   
  30.        DataRowHandler rowHandler = <span style="color: rgb(102, 102, 204);">getDataRowHandler(mimeType)</span>;  
  31.        id = rowHandler.insert(mActiveDb.get(), mTransactionContext.get(), rawContactId, mValues);  
  32.        if (!callerIsSyncAdapter) {  
  33.            mTransactionContext.get().markRawContactDirty(rawContactId);  
  34.        }  
  35.        mTransactionContext.get().rawContactUpdated(rawContactId);  
  36.        return id;  
  37.    }  

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

[java]  view plain copy
  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 plain copy
  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 plain copy
  1. CallLog.java  
  2. Contacts.java  
  3. ContactsContract.java  
  4. SocialContract.java  
  5. SyncConstValue.java  
  6. VoicemailContract.java  

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

[java]  view plain copy
  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 plain copy
  1. AbstractContactsProvider.java  
  2. ContactAggregator.java  
  3. ContactsDatabaseHelper.java  

http://chatgpt.dhexx.cn/article/LW6sEODR.shtml

相关文章

ContactsProvider2

本篇不全也不细&#xff0c;只是根据按照个人理解和工作中遇到的问题&#xff0c;总结了个人认为的要点。 1. Android的数据库体系 1.1. 概述1.2 uri结构 2. ContactsProvider2 2.1. 概述2.2. Contacts2.db中的表2.3. ContactProvider2中的实现2.4. 批量访问 1. Android的数…

Android ContactProvider码源解析

Android Contacts源码解析2 4 ContactsProvider模块 1ContactsProvider简介2数据库创建3主要数据库的表结构相互关系 1data表2raw_contacts表3contacts表4mimetypes5其他表 文章转载自&#xff1a;https://blog.csdn.net/kafka_88/article/details/58585607 4&#xff0c;…

Contacts Provider基础

作为四大组件之一的ContentProvider工作中我们很少会用到自己自定义的ContentProvider,用到的最多的就是系统提供的。官方文档提供了两种系统ContentProvider,一种是CalendarProvider,一种是Contacts Provider。今天我们的主角就是Contact Provider。 The Contacts Provider is…

Error: invalid code, hints: [ req_id: * ]

1、问题描述&#xff1a;这个问题是在处理订单支付时需要用户登录遇到的&#xff0c;具体报错信息如下&#xff1a; 2、报错原因&#xff1a; 当前开发者的appid没有支付权限导致 3、解决方案&#xff1a; 需要负责人给分配对应的权限

This application’s bundle identifier does not match its code signing identifier.

今天使用carthage更新第三方后莫名出现真机云心失败,提示 This application’s bundle identifier does not match its code signing identifier. 解决方法: /usr/local/bin/carthage copy-frameworks 进入Building Phases 单击并在New Run Script Phase中添加脚本 将/usr/lo…

Code Sign error: a valid provisioning profile matching the application's Identifier 'com.yourcompany

出现这个错误的原因是因为&#xff1a;appid和provisioning profile不匹配 &#xff01;有两种解决的办法&#xff1a;重新下载provisioning profile&#xff0c;或者可能因为&#xff1a;生成证书和appid 所用的根证书不同&#xff0c;最好重新都生成然后下载。&#xff08;这…

ANTD react 手机号(验证码)登陆 + 账号登陆(图形验证码)

这种页面可能是大家常用的,但重写比较费时间,之前没有搜到完整的,在这里自己总结一下,方面复用 代码: <LoginFormformRef{formRef}initialValues{{autoLogin: false}}onFinish{async values > {await handleSubmit(values as LoginParams)}}><Tabs activeKey{type…

Springboot+axios+vue使用VerifyCodeUtils工具类实现验证码图片功能

一、环境准备 ideajava 1.8maven 3.6.3操作系统&#xff1a;window10vue.min.jsaxios.min.js 二、VerifyCodeUtils工具类 import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import ja…

Identity and Authentication - Common Authentication Methods

Username and Passwords This is the most common method of identifying users in the age of Software as a Service (SaaS) 解释上面的图中的步骤&#xff1a; Login Request: POST /login postuser {username: users,password: pws }Find users: SELECT * FROM databa…

iOS code signing identity 配置

PROJECT 和 TARGET 中都要选:code signing identity: 调试统一都选 developer, 发布统一都选 distribution provisioning Profile 也要选. 过期和无效的证书及时删除, 避免 ambiguous 警告. 选择正确的证书配置后仍然报错, clean 一下重启 xcode 还不行~~~重启电脑

Invalid CAPTCHA response. Please try again. (Code: 1201)

项目场景&#xff1a; Invalid CAPTCHA response. Please try again. (Code: 1201) 解决方案&#xff1a; 直接使用隐私浏览器打开即可。

经典的Embedding方法Word2vec

提起Embedding,就不得不提Word2vec,它不仅让词向量在自然语言处理领域再度流行&#xff0c;更为关键的是&#xff0c;自2013年谷歌提出Word2vec以来&#xff0c;Embedding 技术从自然语言处理领域推广到广告、搜索、图像、推荐等深度学习应用领域&#xff0c; 成了深度学习知识…

embedding表示方法及原理

目录 1.前言2.embedding表示方法2.1 word2vec embedding2.2 neural network embedding2.3 graph embedding 3.参考文献 1.前言 近几年embedding的使用及优化在各种比赛、论文中都有很多的应用&#xff0c;使用embedding表示特征的空间表示也在各种应用中确定是一种很有效的特征…

深度学习:词嵌入Embedding

http://blog.csdn.net/pipisorry/article/details/76095118 词嵌入 词嵌入其实就是将数据的原始表示表示成模型可处理的或者是更dense的低维表示&#xff08;lz&#xff09;。 One-hot Embedding 假设一共有个物体&#xff0c;每个物体有自己唯一的id&#xff0c;那么从物体…

什么是embedding(把物体编码为一个低维稠密向量),pytorch中nn.Embedding原理及使用

文章目录 使embedding空前流行的word2vec句子的表达训练样本损失函数输入向量表达和输出向量表达 v w v_{w} vw​ 从word2vec到item2vec讨论环节pytorch中nn.Embedding原理及使用pytorch中nn.Embedding原理及使用一些注意的点 参考 简单来说&#xff0c;embedding就是用一个低维…

讲清楚embedding到底在干什么

要搞清楚embeding先要弄明白他和one hot encoding的区别&#xff0c;以及他解决了什么one hot encoding不能解决的问题&#xff0c;带着这两个问题去思考&#xff0c;在看一个简单的计算例子 以下引用 YJango的Word Embedding–介绍 https://zhuanlan.zhihu.com/p/27830489 On…

一文读懂Embedding

文章目录 一、**什么是Embedding&#xff1f;**二、One-Hot编码三、**怎么理解Embedding****四、Word Embedding** 一、什么是Embedding&#xff1f; “Embedding”直译是嵌入式、嵌入层。 简单来说&#xff0c;我们常见的地图就是对于现实地理的Embedding&#xff0c;现实的…

Embedding的理解

Embedding 嵌入&#xff0c;我们可以将其理解为一种降维行为。可以将高维数据映射到低维空间来解决稀疏输入数据的问题。 它主要有以下三个目的&#xff1a; 在 embedding 空间中查找最近邻&#xff0c;这可以很好的用于根据用户的兴趣来进行推荐。作为监督性学习任务的输…

BERT的三个Embedding详解

BERT将输入文本中的每一个词&#xff08;token)送入token embedding层从而将每一个词转换成向量形式 两个嵌入层&#xff0c;segment embeddings和 position embeddings token embedding token embedding 层是要将各个词转换成固定维度的向量。在BERT中&#xff0c;每个词会被…

Embedding层的理解

Embedding层的理解 转载自&#xff1a;原文&#xff1a;深入理解 Embedding层的本质_罗小丰同学的博客-CSDN博客_embedding层 首先&#xff0c;我们有一个one-hot编码的概念。 假设&#xff0c;我们中文&#xff0c;一共只有10个字。。。只是假设啊&#xff0c;那么我们用0-…