APP跨进程数据通信-访问手机联系人
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跨进程数据通信-访问手机联系人的更多相关文章
- Android跨进程通信访问其他应用程序的Activity
访问其他应用程序的ActivityActivity既可以在进程内(同一个应用程序)访问,也可以跨进程访问.如果想在同一个应用程序中访问Activity,需要指定Context对象和Activity的C ...
- 在C#或者SWT上跨进程访问SWT控件的问题
可能为了进程安全,无论是C#的Form还是Eclipse的SWT,都不允许跨进程访问控件. 通俗一点说就是: A进程创建了控件Widget,若想在B进程中访问控件Widget就会报错,必须在创建Wid ...
- 手机APP应用外网访问本地WEB应用
手机APP应用外网访问本地WEB应用 本地安装了WEB服务端,手机APP应用只能在局域网内访问本地WEB,怎样使手机APP应用从公网也能访问本地WEB? 本文将介绍具体的实现步骤. 1. 准备工作 1 ...
- Delphi跨进程访问DBGRID
要想跨进程访问DBGRID,貌似只能用HOOK,写一个DLL想办法注入到目标进程.注入成功后,使DLL与目标进程在同一进程空间中(其内有一些细节问题,请参见代码),这时可以访问目标进程的VCL组件.并 ...
- 跨进程访问VCL的一个用例(Delphi6、TurboDelphi测试通过)
Controls.pas单元中有一个FindControl函数,通过句柄获得对应的TWinControl对象. function FindControl(Handle: HWnd): TWinCont ...
- 跨进程(同一app不同进程之间通信)——Android自动化测试学习历程
视频地址:http://study.163.com/course/courseLearn.htm?courseId=712011#/learn/video?lessonId=877122&co ...
- Android中的跨进程通信方法实例及特点分析(二):ContentProvider
1.ContentProvider简单介绍 在Android中有些数据(如通讯录.音频.视频文件等)是要供非常多应用程序使用的.为了更好地对外提供数据.Android系统给我们提供了Content P ...
- C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 VC中进程与进程之间共享内存 .net环境下跨进程、高频率读写数据 使用C#开发Android应用之WebApp 分布式事务之消息补偿解决方案
C# .Net 多进程同步 通信 共享内存 内存映射文件 Memory Mapped 转 节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing). ...
- Android中的跨进程调用技术AIDL
什么是AIDL Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信. 为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用 ...
随机推荐
- c/c++ SQLite3的常用使用方法;
下面测试用的sqlite例子;大家可以参考使用; #include "CppSQLite3.h" Class TestSqlite{ //定义db指针 private: CppSQ ...
- 【科普】什么是TLS1.3
TLS1.3是一种新的加密协议,它既能提高各地互联网用户的访问速度,又能增强安全性. 我们在访问许多网页的时候,常常会在浏览器的地址栏上看到一个锁的图标,并使用"https"代替传 ...
- 什么是B-Tree
B-Tree就是我们常说的B树,一定不要读成B减树,否则就很丢人了.B树这种数据结构常常用于实现数据库索引,因为它的查找效率比较高. B-Tree与二叉查找树的对比 我们知道二叉查找树查询的时间复杂度 ...
- java内存区域——深入理解JVM读书笔记
本内容由<深入理解java虚拟机>的部分读书笔记整理而成,本读者计划连载. 通过如下图和文字介绍来了解几个运行时数据区的概念. 方法区:它是各个线程共享的区域,用于内存已被VM加载的类信息 ...
- 【TRICK】解决锚点定位向下浮动Xpx问题
1. 问题描述 页面滚动后,菜单栏会固定在页头,当锚点定位时,菜单会遮盖部分定位后的内容,所以需要在锚点定位后自动向下漂移Xpx. 2. 解决办法 a. 利用空div 占位,如下: <a hre ...
- 【mysql】常用操作
2.mysql service mysql status mysql --version mysql -h 服务器主机地址 -u 用户名 -p 用户密码 exit 退出 mysql -h 主机名 - ...
- Verilog HDL程序设计——基本要素
Verilog基本上熟悉了,继续整理一下Verilog的学习笔记吧.前面记载了Verilog的结构,写Verilog的结构有了,但是该怎么写呢?在写之前就得了解一下Verilog的一些基本要素了,也就 ...
- git 安装 和 基本操作
林纳斯的小故事 感兴趣的同学可以自己百度一下 版本控制常用svn git @@@svn 1 搭建环境 server:visualSVN Serverserver port: https 默认443ht ...
- 使用maven搭建环境
今天第一次用maven创建springmvc工程,下载配置都很成功,但用命令行创建项目时遇到一些问题: 1.命令行显示命令不为内部或外部命令: 解决方法:使用管理员模式打开命令行 2. 显示到如下图所 ...
- Golang使用pprof和qcachegrind进行性能监控
Golang为我们提供了非常方便的性能测试工具pprof,使用pprof可以非常方便地对Go程序的运行效率进行监测.本文讲述如何使用pprof对Go程序进行性能测试,并使用qcachegrind查看性 ...