# ContentProvider
**Repository Path**: haojie_xuexi/ContentProvider
## Basic Information
- **Project Name**: ContentProvider
- **Description**: ContentResolver实现系统数据库的操作
访问系统短信
访问通话记录
- **Primary Language**: Android
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 1
- **Created**: 2018-10-11
- **Last Updated**: 2022-05-07
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# ContentProvider
#### 项目介绍
ContentResolver实现系统数据库的操作
访问系统短信
访问通话记录
> ContentProvider
ContentResolver实现系统数据库的操作
ContentProvider与ContentResolver的概念及关系
ContentProvider:为存储和获取数据提供统一的接口。可以在不同的应用程序之间共享数据。Android已经为常见的一些数据提供了默认的ContentProvider。
当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。虽然使用其他方法也可以对外共享数据,但数据访问方式会因数据存储的方式而不同,如:采用文件方式对外共享数据,需要进行文件操作读写数据;而使用ContentProvider共享数据的好处是统一了数据访问方式。
可以这么理解:ContentProvider负责暴露数据、ContentResolver操作访问暴露出来的数据
简单了解URI
每个Content Provider提供公共的URI来唯一标识其数据集,管理多个数据集(多个表格)的Content Provider为每个都提供了单独的URI。所提供的URI都以content://作为前缀”content://”模式表示数据由Content Provider来管理。
如果自定义Content Provider,则应该也为URI定义一个常量,以简化客户端代码并让日后更新更加简洁。Android为当前平台提供的Content Provider定义了CONTENT_URI常量。
常用的如下:
访问音频格式的多媒体文件的Uri
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
访问系统短信的Uri
Uri.parse(“content://sms”);
访问通话记录的Uri
Uri.parse("content://call_log/calls");
访问通讯录的Uri
// raw_contacts表的uri:
// content://com.android.contacts/raw_contacts
Uri rawContactUri = RawContacts.CONTENT_URI;--结合数据库
// data表的uri
// content://com.android.contacts/data
Uri dataUri = ContactsContract.Data.CONTENT_URI
利用ContentResolver实现系统数据的操作(联系人【查询】、媒体库文件、通话记录、短信记录);提示:一些系统的常量不需要大家记得很熟,只要了解知道有这个东西存在,需要用到时候可以适当查一下资料即可。
访问音频
访问音频的URI:MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
查询音频文件信息表的相关列名:
Audio.Media.DISPLAY_NAME:文件全名
Audio.Media.DATA:文件路径
Audio.Media.TITLE:文件标题
---系统会去访问添加过手机的音乐。默认在外部存储的Music文件夹,测试的时候添加几首音乐到Music目录去测试
需要添加读写外部存储的权限
访问通话记录
访问通话记录的URI:Uri.parse("content://call_log/calls");
查询通话记录信息表的相关列名:
CallLog.Calls._ID:主键 ---int类型
CallLog.Calls.NUMBER:电话号码--String类型
CallLog.Calls.DATE:通话时间 ---long类型
CallLog.Calls.TYPE:通话类型 ---int类型
需要添加读取通话记录的权限
访问系统短信
访问系统短信的URI:Uri.parse(“content://sms”);
查询系统短信信息表的相关列名:
Telephony.Sms.ADDRESS:短信号码 --字符串类型
Telephony.Sms.BODY:短信内容 ---字符串类型
Telephony.Sms.TYPE:短信类型 ---int类型
Telephony.Sms.DATE:短信类型--long类型
需要添加读取短信的权限
访问系统通讯录---(多表结构,会比较麻烦)
添加应用权限读写联系人权限:
了解联系人的数据表:
raw_contacts表:
data表:
mimetypes表:
获取联系人信息:
访问系统通讯录的URI:RawContacts.CONTENT_URI
查询系统通讯录信息表的相关列名:
"_id":联系人ID(通过ID查询该联系人data表下的所有数据包含mimetypes)
通过联系人ID继续访问联系人下面的所有数据的URI:ContactsContract.Data.CONTENT_URI
content://com.android.contacts/contacts/联系人ID/data"
查询系统通讯录信息表的相关列名:
"mimetype":mimetype类型(参照mimetypes表)
"data1":相应类型的数据(手机号码,姓名,Email等)
"data2":多个手机号码或者Email的序号。
***等等...
String data=dataCursor.getString(dataCursor.getColumnIndex("data1"));
String data=dataCursor.getString(dataCursor.getColumnIndex("data2"));
String type=dataCursor.getString(dataCursor.getColumnIndex("mimetype"));
if("vnd.android.cursor.item/name".equals(type))
{ // 如果他的mimetype类型是name
String name=data;
} elseif("vnd.android.cursor.item/email_v2".equals(type))
{ // 如果他的mimetype类型是email
String email=data;
} elseif("vnd.android.cursor.item/phone_v2".equals(type))
{ // 如果他的mimetype类型是phone
String phone=data;
}
添加联系人信息:
添加通讯录的URI:ContactsContract.Data.CONTENT_URI
// 步骤:1、先往raw_contacts表中添加一条空白的数据,目的是先获取到新的联系人的id
ContentValues values = new ContentValues();
Uri insert = resolver.insert(ContactsContract.CONTENT_URI, values);
longid = ContentUris.parseId(insert);
// 步骤2:往data表中先插入对应的数据。
values.clear();
values.put("raw_contact_id", id);
values.put("mimetype", "vnd.android.cursor.item/name");
values.put("data1", name);
values.put("data2", name);//如果是名字,系统默认是和data1一样。
resolver.insert(ContactsContract.Data.CONTENT_URI, values);
values.clear();
values.put("raw_contact_id", id);//添加某个新的联系人
values.put("mimetype", "vnd.android.cursor.item/phone_v2");
values.put("data1", phone);//插入的手机号码
values.put("data2", "2");//表示第几个手机号码
resolver.insert(ContactsContract.Data.CONTENT_URI, values);
values.clear();
values.put("raw_contact_id", id);
values.put("mimetype", "vnd.android.cursor.item/email_v2");
values.put("data1", email);
values.put("data2", "2");
resolver.insert(ContactsContract.Data.CONTENT_URI, values);
删除联系人信息:
//直接删除制定的ID即可
resolver.delete(ContactsContract.Data.CONTENT_URI, "contact_id = ?", new String[] { id })
修改联系人信息:(以修改名称为例,其他大同小异)
// 更新联系人号码
publicboolean updataCotact(String id, String newName) {
ContentValues values = new ContentValues();
values.put("data1", newName);
// 修改条件,对应的联系人ID,对应的修改数据类型。如果是号码或者Email有多个数据的话,需要对data2或者data1条件判断.(否则会覆盖所有号码或Email)
String where = "raw_contact_id =? AND mimetype =? And data2 = 2";
String[] selectionArgs = new String[] { id, "vnd.android.cursor.item/phone_v2" };
intupdate = getContentResolver().update(dataUri, values, where, selectionArgs);
returnupdate> 0;
}
自定义ContentProvider
自定义ContentProvider的实现步骤
1、建立数据存储系统,大多数ContentProvider使用Android的SQLite数据库保存数据或者文件存储方法。
2、继承ContentProvider类来提供数据访问方式。
ContentProvider的子类需要重写以下方法
// 在启动的时候,用来初始化我们的内容提供者;
// 在我们的应用程序已启动的时候,系统会自动的调用这个方法。
// 在这个方法中,不应该执行耗时的操作,否则系统的启动会有所延迟。
// 需要注意的是必须把onCreate方法的返回值改为true,该ContentProvider才能被加载。
publicboolean onCreate() {
returnfalse;
}
// 查询,返回Cursor对象
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
returnnull;
}
// 插入,返回Uri,一般开发者返回插入后该条记录的完整Uri。使用ContentUris.withAppendedId(uri, id),在URI后面追加新增的ID,作为完整的Uri返回。
public Uri insert(Uri uri, ContentValues values) {
returnnull;
}
// 删除,返回删除成功的行数
publicint delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
// 查询,返回修改成功的行数
publicint update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
// 这个方法,让开发者根据传入的URI,来返回相应数据的MIME类型。
// 内容提供者多数用在开发兄弟App中,一般是根据开发者的需求。实际开发中比较少用,可以直接返回null。
@Override
public String getType(Uri uri) {
intmatch = mUriMatcher.match(uri);
switch (match) {
caseUri后面没有ID的匹配类型:// 表示多行数据类型
return"vnd.android.cursor.dir/";
caseUri后面有ID的匹配类型:// 表示单行数据类型
return"vnd.android.cursor.item/";
default:
break;
}
returnnull;
}
URI的用法
URI常量用于所有与Content Provider的交互中.每个ContentResolver方法使用URI作为其第一个参数.它标识ContentResolver应该使用哪个ContentProvider以及其中的哪个表格.
下面是Content URI重要部分的总结
content://com.1000phone.user/student/1
上面的URI分四部分
第一部分:标准的前缀,用于标识该数据由Content Provider管理,它永远不用修改。
第二部分:URI的authority部分,它标识该Content Provider,对于第三方应用,该部分应该是完整的类名(小写)来保证唯一性,在元素的authorities属性中声明authority。
第三部分: Content Provider的路径部分,用于决定哪类数据被请求,如果Content Provider仅提供一种数据类型,这部分可以没有。如果提供几种类型,包括子类型,这部分可以由几部分组成。
第四部分:被请求的特定的记录的ID值,这是被请求记录的_ID值,如果请求不仅限于单条记录该部分及前面的斜线应该删除。
UriMatcher 的使用
// NO_MATCH=-1;就是一个默认的返回码.如果URI(口令)不匹配,系统就会返回这个NO_MATCH.
static UriMatcher um= new UriMatcher(UriMatcher.NO_MATCH);
// addURI()就是用来创建URI(口令)的.
// 3个参数:authority:口令的上半句;path:口令的下半句;code:当用户输入的URI(口令)匹配的时候,那么就会返回这个code。
um.addURI(authority, path, code);
比如:
//content://com.1000phone.provider/student
um.addURI("com.1000phone.provider", "student", 110);
//content://com.1000phone.provider/student/18
um.addURI("com.1000phone.provider", "student/#", 120);
//content://com.1000phone.provider/student/name/张三
um.addURI("com.1000phone.provider", "student/name/*", 119);
// match()就是用来匹配URI(口令)的.
//根据用户输入的URI(口令)匹配,返回addURI()创建的URI(口令)的参数三。
um.match(uri);
比如:
intmatch = um.match("content://com.1000phone.provider/student/18");
则:match==120
ContentUris 的使用
// withAppendedId()就是用来给URI(口令)追加id的。
// 参数contentUri:指需要追加的URI(口令);id:追加的ID
ContentUris.withAppendedId(contentUri, id);
比如:
ContentUris.withAppendedId(Uri.parse("content://com.1000phone.provider/student"), 5);
则:
返回新的Uri(口令):"content://com.1000phone.provider/student/5"
// parseId()就是用来解析URI(口令)后面的id值。
// 参数contentUri:指需要解析的URI(口令);
ContentUris.parseId(contentUri);
比如:
ContentUris.parseId(Uri.parse("content://com.1000phone.provider/student/18"));
则:
返回Uri(口令)后面的ID:18
uri对象的使用
// content://org.mobiletrain/person/filter/name
// getLastPathSegment()方法是用来截取uri中最后一部分的字符串
String segment= uri.getLastPathSegment();
比如:
Uri.parse("content://com.1000phone.provider/student/name/张三").getLastPathSegment();
则:
返回Uri(口令)最后一个’/’后面的字符串片段:张三