# 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(口令)最后一个’/’后面的字符串片段:张三