1. 简述

在实际开发中,常常需要进行不同应用程序之间的数据通信,例如读取联系人列表等等,ContentProvider就是Android提供的用于实现不同进程之间进行数据通信的类。

ContentProvider的作用是对外提供对本应用的数据进行“增删改查”的接口,而后在其它程序可通过ContentResolver类访问提供的接口,从而实现跨应用数据通信。

2. ContentProvider类

首先,ContentProvider与其它几个组件一样也是一个抽象类,使用时必须实现一些方法,并且也需要在manifest中进行注册。

另外,如果学过SQLite的操作,你会发现增删改查几个方法与SQLite数据库的几个操作方法极为相似。

注册:

<provider
android:name=".MyContentProvider"
android:authorities="com.studying.myprovider"
android:enabled="true"
android:exported="true" />

PS:provider标签写在application中,与activity同级。

实现的方法:

boolean onCreate():在创建ContentProvider时会调用,在这里完成一些初始化的操作,注意要返回true。

Uri insert(Uri uri, ContentValues values):添加方法。

关于Uri:参数中的uri是数据接收方指定哪一个ContentProvider的依据,格式为"content://authorities[/path]",authorities即manifest中注册provider时的属性,命名规则类似Java的包名,path则为路径,可以没有,例如"content://com.studying.myprovider"。(关于Uri的解析后面会介绍)

第二个参数values则是插入的值包,其中key值需与数据库中的列名保持一致。另外,insert()方法的返回值为一个Uri,可以在传入的Uri后加上成功插入的记录的ID值作为返回的Uri,可用于判断是否插入成功。添加ID与取出ID的方法为:

ContentUris.withAppendedId(Uri contentUri, long id)// 把id追加到contentUri后面
ContentUris.parseId(Uri uri)// 将id取出

int delete(Uri uri, String selection, String[] selectionArgs):selection为条件,selectionArgs为条件值,返回值为受操作影响的行数,例如删除了1行,则返回1。

int update(Uri uri, ContentValues values, String selection, String[] selectionArgs):返回值同delete()方法。

Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder):projection为查询的列,sortOrder为排序方法,query()方法返回一个Cursor对象。

String getType(Uri uri):返回数据的MIME类型,这个方法一般用不到,详情可参考:ContentProvider数据库共享之——MIME类型与getType()

3. ContentResolver类:

应用程序提供数据共享的接口时使用ContentProvider类,而需要访问其它应用共享的数据时,则需要使用ContentResolver类。

获取ContentResolver对象的方法:在Activity中直接getContentResolver()方法即返回一个ContentResolver对象。

使用方法非常简单,调用ContentResolver对象中的insert()等方法会调用Uri匹配到的ContentProvider中的相应方法。

简单例子:

ContentValues values = new ContentValues();
values.put("name", name);
values.put("age", age);
values.put("sex", sex);
Uri returnUri = resolver.insert(Uri.parse("content://com.studying.myprovider"), values);
long newItemId = ContentUris.parseId(returnUri);
Toast.makeText(this, "添加成功,新增加的学生ID为" + newItemId, Toast.LENGTH_SHORT).show();

4. URI的解析

URI即Uniform Resource Identifier 统一资源标识符,在这里可以标识访问哪一个ContentProvider,除此之外,还可以传递参数,以及通过匹配UriMatcher类制定的不同匹配规则进行相应处理。

(1)UriMatcher类:

UriMatcher类可以制定URI的匹配规则,然后可以通过在使用resolver对象时传入不同格式的URI,使ContentProvider类做出不同的处理。

a)创建匹配规则:matcher.addURI(authorities, path, code),path为路径,可使用#表示任意数字,*表示任意字符;code则为匹配码。

b)在对应方法中匹配:int code = matcher.match(uri),返回的code即为匹配码。

简单例子:

首先在ContentProvider的onCreate()方法中制定规则:

matcher = new UriMatcher(UriMatcher.NO_MATCH);// 当所有匹配情况都无法匹配到时,则返回UriMatcher.NO_MATCH
matcher.addURI("com.studying.myprovider", "test1/#", 1001);
matcher.addURI("com.studying.myprovider", "test2/*", 1002);

然后在处理方法中做不同处理:

int columnCount = 0;
switch (matcher.match(uri)) {
case 1001:
Log.e("TAG", "匹配成功!匹配形式为:test1 + 任意数字");
break;
case 1002:
Log.e("TAG", "匹配成功!匹配形式为:test2 + 任意字符串");
break;
default:
columnCount = db.delete(TABLE_NAME, selection, selectionArgs);
Log.e("TAG", "删除成功!");
break;
}

最后通过ContentResolver传入需要的Uri进行使用:

ContentResolver resolver = getContentResolver();
resolver.delete(Uri.parse("content://com.studying.myprovider/test1/132"), null, null); // 匹配到1001
resolver.delete(Uri.parse("content://com.studying.myprovider/test2/1ac2"), null, null); // 匹配到1002
resolver.delete(Uri.parse("content://com.studying.myprovider/12"), null, null); // 匹配不到,则为UriMatcher.NO_MATCH

(2)Uri自带解析方法

除了UriMatcher类,还可以通过Uri类自带的解析方法进行传值,传递参数的方法是:在Uri末尾加上“?”,而后后面加上参数,多个参数之间以“&”连接,例如"content://com.studying.myprovider?name=Tim&age=22"。

在Uri传递到ContentProvider之后,通过以下方法取出需要的部分:

uri.getAuthorities():获取authorities部分。

uri.getPath():获取path部分。

uri.getQuery():获取“?”后面的全部字符串。

uri.getQueryParameter(parameterName):传入相应参数名,获取该参数值。例如String name = uri.getQueryParameter("name");

5. 访问手机短信箱

访问短信箱的方式非常简单,跟上面例子的步骤基本一致,只需要额外到manifest中添加访问短消息的权限即可。

读取权限:android.permission.READ_SMS

写入:android.permission.WRITE_SMS

Uri:

短信箱(全部短消息,包括发送的、接收的以及草稿):content://sms

收件箱:content://sms/inbox

发件箱:content://sms/sent

草稿箱:content://sms/draft

ContentResolver resolver = getContentResolver();
Uri smsUri = Uri.parse("content://sms/inbox");
Cursor c = resolver.query(smsUri, null, null, null, null);
while (c != null && c.moveToNext()) {
// 3和13分别是号码和短消息内容所在的列的索引
Log.e("TAG", c.getString(3) + " " + c.getString(13));
}

6. 读取联系人列表

与短消息不同,安卓中存储联系人的方式相对比较复杂,联系人的姓名和号码是分开存储的。简单地理解,可以想象成是数据库的形式,姓名和号码分别存放在两张数据表中,而互相之间通过一个唯一的ID值进行标识。因此,读取联系人需要先获取姓名和ID,再通过ID去获取号码。

权限:android.permission.READ_CONTACTS

姓名所在的ContentProvider的URI:ContactsContract.Contacts.CONTENT_URI

姓名列的列名常量:ContactsContract.Contacts.DISPALY_NAME

ID列的列名常量:ContactsContract.Contacts._ID (PS:切勿遗漏了下划线)

号码所在ContentProvider的URI:ContactsContract.CommonDataKinds.Phone.CONTENT_URI

号码外键列的列名常量:ContactsContract.CommonDataKinds.Phone.CONTACT_ID

号码列的列名常量:ContactsContract.CommonDataKinds.Phone.NUMBER

// 首先获取姓名和ID
ContentResolver resolver = getContentResolver();
Cursor nameCursor = resolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
while (nameCursor != null && nameCursor.moveToNext()) {
String name = nameCursor.getString(nameCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
String _id = nameCursor.getString(nameCursor.getColumnIndex(ContactsContract.Contacts._ID)); // 再通过ID获取相应的号码
String selections = ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?";
Cursor numberCursor = resolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, selections, new String[] {_id}, null);
String result = "";
while (numberCursor != null && numberCursor.moveToNext()) {
String number = numberCursor.getString(numberCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
result += name + " " + number + " ";
} Log.e("RESULT", result);
}

7. 添加联系人

读取联系人时需要分开读取,那么写入当然也要分开写入。首先向一个存储了一些其它数据的ContentProvider中插入一个空数据,从而获取到一个新的ID,再通过这个ID分别插入姓名和号码(因为每次插入时都需要指定插入数据的类型,因此需要分开插入)。

权限:android.permission.WRITE_CONTACTS

Uri

获取ID的URI:ContactsContract.RawContacts.CONTENT_URI

插入数据的URI:ContactsContract.Data.CONTENT_URI

需要使用的常量

姓名的列名:ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME

ID的列名:ContactsContract.Data.RAW_CONTACT_ID

电话号码的列名:ContactsContract.CommonDataKinds.Phone.NUMBER

指定MIME类型的列名:ContactsContract.Data.MIMETYPE

姓名的MIME类型:ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE

电话号码的MIME类型:ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE

电话号码类型的列名:ContactsContract.CommonDataKinds.Phone.TYPE

手机号码:ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE

住宅号码:ContactsContract.CommonDataKinds.Phone.TYPE_HOME

PS:常量非常多,要留意其所在的包,共用的一些常量位于ContactsContract.Data包下,例如ID、数据类型等,姓名相关的则在ContactsContract.CommonDataKinds.StructuredName包下,电话号码相关的则在ContactsContract.CommonDataKinds.Phone包下,理清之后就容易记住了。

ContentResolver resolver = getContentResolver();

// 获取一个新的ID
ContentValues values = new ContentValues();
Uri idUri = resolver.insert(ContactsContract.RawContacts.CONTENT_URI, values);
long id = ContentUris.parseId(idUri); // 插入姓名
values.put(ContactsContract.Data.RAW_CONTACT_ID, id);
values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, "Jay");
// 需要指定姓名的数据类型,也就是getType()方法将来返回的值
values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
resolver.insert(ContactsContract.Data.CONTENT_URI, values); // 插入号码
values.clear();
values.put(ContactsContract.Data.RAW_CONTACT_ID, id);
values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, "13511223344");
// 指定联系号码的类型,如手机号码、住宅号码等
values.put(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE);
values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
resolver.insert(ContactsContract.Data.CONTENT_URI, values);

APP跨进程数据通信-访问手机联系人的更多相关文章

  1. Android跨进程通信访问其他应用程序的Activity

    访问其他应用程序的ActivityActivity既可以在进程内(同一个应用程序)访问,也可以跨进程访问.如果想在同一个应用程序中访问Activity,需要指定Context对象和Activity的C ...

  2. 在C#或者SWT上跨进程访问SWT控件的问题

    可能为了进程安全,无论是C#的Form还是Eclipse的SWT,都不允许跨进程访问控件. 通俗一点说就是: A进程创建了控件Widget,若想在B进程中访问控件Widget就会报错,必须在创建Wid ...

  3. 手机APP应用外网访问本地WEB应用

    手机APP应用外网访问本地WEB应用 本地安装了WEB服务端,手机APP应用只能在局域网内访问本地WEB,怎样使手机APP应用从公网也能访问本地WEB? 本文将介绍具体的实现步骤. 1. 准备工作 1 ...

  4. Delphi跨进程访问DBGRID

    要想跨进程访问DBGRID,貌似只能用HOOK,写一个DLL想办法注入到目标进程.注入成功后,使DLL与目标进程在同一进程空间中(其内有一些细节问题,请参见代码),这时可以访问目标进程的VCL组件.并 ...

  5. 跨进程访问VCL的一个用例(Delphi6、TurboDelphi测试通过)

    Controls.pas单元中有一个FindControl函数,通过句柄获得对应的TWinControl对象. function FindControl(Handle: HWnd): TWinCont ...

  6. 跨进程(同一app不同进程之间通信)——Android自动化测试学习历程

    视频地址:http://study.163.com/course/courseLearn.htm?courseId=712011#/learn/video?lessonId=877122&co ...

  7. Android中的跨进程通信方法实例及特点分析(二):ContentProvider

    1.ContentProvider简单介绍 在Android中有些数据(如通讯录.音频.视频文件等)是要供非常多应用程序使用的.为了更好地对外提供数据.Android系统给我们提供了Content P ...

  8. C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 VC中进程与进程之间共享内存 .net环境下跨进程、高频率读写数据 使用C#开发Android应用之WebApp 分布式事务之消息补偿解决方案

    C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing). ...

  9. Android中的跨进程调用技术AIDL

    什么是AIDL Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信. 为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用 ...

随机推荐

  1. Apache Flume 1.7.0 各个模块简介

    Flume简介 Apache Flume是一个分布式.可靠.高可用的日志收集系统,支持各种各样的数据来源,如http,log文件,jms,监听端口数据等等,能将这些数据源的海量日志数据进行高效收集.聚 ...

  2. 利用Unity3D与Oculus实现机器情绪安抚师的一种方案

    (一张最原始的Unity3D中音乐可视化粒子海的图,想象一下,如果这幅场景出现在虚拟设备中,辅以根据音乐频谱变化的色彩与悦动频率,会是怎样的效果呢?) Unity3D有着非常完备的虚拟三维场景交互开发 ...

  3. YII2 添加全局自定义函数

    方法一: 这种方法就是直接在入口文件web/index.php里面写函数,示例代码如下: 全局函数 function pr($var){ //do something } (new yii\web\A ...

  4. 蓝桥杯比赛java 练习《立方变自身》

    立方变自身 观察下面的现象,某个数字的立方,按位累加仍然等于自身.1^3 = 1 8^3  = 512    5+1+2=817^3 = 4913   4+9+1+3=17... 请你计算包括1,8, ...

  5. Hibernate批量操作(一)

    在项目的开发过程之中,我们常会遇到数据的批量处理问题.在持久层采用Hibernate框架时,在进行批量操作时,需要考虑Hibernate实现机制带来的一些问题. 我们知道在每个Hibernate Se ...

  6. MySql学习笔记(四)

    MYSQL如何查看系统帮助: 1.查看官方API文档: http://dev.mysql.com/doc/ 2.通过Mysql中的help命令 比如:help create database MYSQ ...

  7. [小北De编程手记] Lesson 01 - AutoFramework构建 之 从一个简单的Demo聊起

    写在最前面 这个系列的主旨是要跟大家分享一下关于自动化测试框架的构建的一些心得.这几年,做了一些自动化测试框架以及团队的构建的工作.过程中遇到了很多这样的同学,他们在学习了某一门语言和一些自动化测试的 ...

  8. React Native 系列(四) -- 布局

    前言 本系列是基于React Native版本号0.44.3写的.RN支持CSS中的布局属性,因此可以使用CSS布局属性,这里就不详细地讲解了,这篇文章的重点主要是讲述一下RN中的Flex布局. CS ...

  9. css小随笔(二)与通用样式

    51先在学校HTML5已经有半个多月了,然后这个星期做了一个京东的手机网站,接触到了通用样式,下面以京东的手机站为例 这两个就是京东手机站了的不同的两个板块,因为HTML5仅仅只是学完了基本标签跟cs ...

  10. HTML 贝塞尔曲线

    1.二次贝塞尔曲线 <canvas id="myCanvas" width="300" height="150" style=&quo ...