ContentProvider

前面几节介绍了进程间通信的几种方式,包括消息包级别的Messenger、接口调用级别的AIDL、启动页面/服务级别的Notification,还有就是本节这个数据库级别的ContentProvider。
ContentProvider为存取数据提供统一的接口,它让不同APP之间得以共享数据。ContentProvider类本身是个服务端的数据存取接口,主要操作类似SQLite,也都提供了如下常见的数据库管理API:
query : 查询数据。
insert : 插入数据。
update : 更新数据。
delete : 删除数据。
getType : 获取数据类型。
实际开发中,APP很少会开放数据接口给其他应用,所以ContentProvider类作为服务端接口反而基本用不到。Content组件中能够用到的场合,基本上是APP想要使用系统的手机通讯数据,比如查看联系人/短信/彩信/通话记录,以及对这些通讯信息进行增删改。

ContentResolver

使用说明

ContentResolver是客户端APP用来操作服务端数据的接口,相对应的ContentProvider是服务端的接口。获取一个ContentResolver对象,调用Context.getContentResolver()即可。
与ContentProvider一样,客户端的ContentResolver也提供了query、insert、update、delete、getType等等方法。其中最常用的是query函数,调用该函数返回一个Cursor对象,有关Cursor的操作参见《 Android开发笔记(三十一)SQLite游标及其数据结构》。下面是query的具体参数说明:
uri : Uri类型,可以理解为本次操作的数据表路径
projection : String[]类型,指定将要查询的字段名列表
selection : String类型,指定查询条件
selectionArgs : String[]类型,指定查询条件中的参数取值列表
sortOrder : String类型,指定排序条件

下面是ContentResolver在查看通讯信息中的具体运用:

读取联系人

代码示例如下:

 private static Uri mContactUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;private static String[] mContactColumn = new String[] {ContactsContract.CommonDataKinds.Phone.NUMBER,ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME };public static int readPhoneContacts(ContentResolver resolver) {ArrayList<Contact> contactArray = new ArrayList<Contact>();Cursor cursor = resolver.query(mContactUri, mContactColumn, null, null, null);if (cursor.moveToFirst()) {for (;; cursor.moveToNext()) {Contact contact = new Contact();contact.phone = cursor.getString(0).replace("+86", "").replace(" ", "");contact.name = cursor.getString(1);Log.d(TAG, contact.name+" "+contact.phone);contactArray.add(contact);if (cursor.isLast() == true) {break;}}}cursor.close();return contactArray.size();}

上面代码获取的是手机里的联系人。获取SIM卡上的联系人与之类似,不同之处要把Uri换成“content://icc/adn”。

读取短信

代码示例如下:

 private static Uri mSmsUri;private static String[] mSmsColumn;@TargetApi(Build.VERSION_CODES.KITKAT)public static int readSms(ContentResolver resolver, String phone, int gaps) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {mSmsUri = Telephony.Sms.Inbox.CONTENT_URI;mSmsColumn = new String[] { Telephony.Sms.ADDRESS, Telephony.Sms.PERSON,Telephony.Sms.BODY, Telephony.Sms.DATE,Telephony.Sms.TYPE};} else {mSmsUri = Uri.parse("content://sms/inbox");mSmsColumn = new String[] { "address","person","body","date","type" };}ArrayList<SmsContent> smsArray = new ArrayList<SmsContent>();String selection = "";if (phone!=null && phone.length()>0) {selection = String.format("address='%s'", phone);}if (gaps > 0) {selection = String.format("%s%sdate>%d", selection, (selection.length()>0)?" and ":"", System.currentTimeMillis()-gaps*1000);}Cursor cursor = resolver.query(mSmsUri, mSmsColumn, selection, null, "date desc");if (cursor.moveToFirst()) {for (;; cursor.moveToNext()) {SmsContent sms = new SmsContent();sms.address = cursor.getString(0);sms.person = cursor.getString(1);sms.body = cursor.getString(2);sms.date = formatDate(cursor.getLong(3));sms.type = cursor.getInt(4);  //type=1表示收到的短信,type=2表示发送的短信Log.d(TAG, sms.address+" "+sms.person+" "+sms.date+" "+sms.type+" "+sms.body);smsArray.add(sms);if (cursor.isLast() == true) {break;}}}cursor.close();return smsArray.size();}

读取彩信

代码示例如下:

 private static Uri mMmsUri;private static String[] mMmsColumn;@TargetApi(Build.VERSION_CODES.KITKAT)public static int readMms(ContentResolver resolver, int gaps) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {mMmsUri = Telephony.Mms.Inbox.CONTENT_URI;mMmsColumn = new String[] { Telephony.Mms.DATE, Telephony.Mms.READ,Telephony.Mms.SUBJECT, Telephony.Mms.EXPIRY,Telephony.Mms.STATUS, Telephony.Mms.MESSAGE_SIZE};} else {mMmsUri = Uri.parse("content://mms/inbox");mMmsColumn = new String[] { "date","read","sub","exp","st","m_size" };}ArrayList<MmsContent> mmsArray = new ArrayList<MmsContent>();String selection = String.format("date>%d", System.currentTimeMillis()-gaps*1000);Cursor cursor = resolver.query(mMmsUri, mMmsColumn, selection, null, "date desc");if (cursor.moveToFirst()) {for (;; cursor.moveToNext()) {MmsContent mms = new MmsContent();mms.date = formatDate(cursor.getLong(0));mms.read = cursor.getString(1);mms.subject = cursor.getString(2);mms.expire = cursor.getString(3);mms.status = cursor.getString(4);mms.message_size = cursor.getString(5);Log.d(TAG, mms.date+" "+mms.read+" "+mms.subject+" "+mms.expire+" "+mms.status+" "+mms.message_size);mmsArray.add(mms);if (cursor.isLast() == true) {break;}}}cursor.close();return mmsArray.size();}

读取通话记录

代码示例如下:

 private static Uri mRecordUri = CallLog.Calls.CONTENT_URI;private static String[] mRecordColumn = new String[] { CallLog.Calls.CACHED_NAME, CallLog.Calls.NUMBER, CallLog.Calls.TYPE,CallLog.Calls.DATE, CallLog.Calls.DURATION, CallLog.Calls.NEW };public static int readCallRecord(ContentResolver resolver, int gaps) {ArrayList<CallRecord> recordArray = new ArrayList<CallRecord>();String selection = String.format("date>%d", System.currentTimeMillis()-gaps*1000);Cursor cursor = resolver.query(mRecordUri, mRecordColumn, selection, null, "date desc");if (cursor.moveToFirst()) {for (;; cursor.moveToNext()) {CallRecord record = new CallRecord();record.name = cursor.getString(0);record.phone = cursor.getString(1);record.type = cursor.getInt(2);  //type=1表示接听,2表示拨出,3表示未接record.date = formatDate(cursor.getLong(3));record.duration = cursor.getLong(4);record._new = cursor.getInt(5);Log.d(TAG, record.name+" "+record.phone+" "+record.type+" "+record.date+" "+record.duration);recordArray.add(record);if (cursor.isLast() == true) {break;}}}cursor.close();return recordArray.size();}

ContentProviderOperation

使用说明

前面说过,ContentResolver可以由客户端用来给服务端添加数据,不过有时候某种数据在服务端对应的是多张表,比如说联系人信息在服务端实际有联系人姓名表、联系人电话表(因为有家庭电话、工作电话之分)、联系人电子邮箱表。对于这种情况,使用ContentResolver固然可以通过多次插入来实现,可是多次插入就对应多个事务,一旦某次插入失败,那我们还得手工进行回滚操作,非常麻烦。
针对上面的问题,Android提供了ContentProviderOperation类,用于在一个事务中批量插入多条记录,这样即使出现失败,也会由ContentProviderOperation统一处理回滚事宜,避免了开发者关注内部事务的麻烦。

下面是两种插入方式在添加联系人信息中的具体运用:

ContentResolver方式

代码示例如下:

public static void addContacts(ContentResolver resolver) {//往 raw_contacts 中添加数据,并获取添加的id号Uri raw_uri = Uri.parse("content://com.android.contacts/raw_contacts");ContentValues values = new ContentValues();long contactId = ContentUris.parseId(resolver.insert(raw_uri, values));//往 data 中添加数据(要根据前面获取的id号)Uri uri = Uri.parse("content://com.android.contacts/data");ContentValues name = new ContentValues();name.put("raw_contact_id", contactId);name.put("mimetype", "vnd.android.cursor.item/name");name.put("data2", "阿四");resolver.insert(uri, name);ContentValues phone = new ContentValues();phone.put("raw_contact_id", contactId);phone.put("mimetype", "vnd.android.cursor.item/phone_v2");phone.put("data2", "2");phone.put("data1", "15960238696");resolver.insert(uri, phone);ContentValues email = new ContentValues();email.put("raw_contact_id", contactId);email.put("mimetype", "vnd.android.cursor.item/email_v2");email.put("data2", "2");email.put("data1", "aaa@163.com");resolver.insert(uri, email);
}

ContentProviderOperation方式

代码示例如下:

public static void addFullContacts(ContentResolver resolver) {Uri raw_uri = Uri.parse("content://com.android.contacts/raw_contacts");Uri uri = Uri.parse("content://com.android.contacts/data");ContentProviderOperation op_main = ContentProviderOperation.newInsert(raw_uri).withValue("account_name", null).build();ContentProviderOperation op_name = ContentProviderOperation.newInsert(uri).withValueBackReference("raw_contact_id", 0).withValue("mimetype", "vnd.android.cursor.item/name").withValue("data2", "阿三").build();ContentProviderOperation op_phone = ContentProviderOperation.newInsert(uri).withValueBackReference("raw_contact_id", 0).withValue("mimetype", "vnd.android.cursor.item/phone_v2").withValue("data2", "2").withValue("data1", "15960238696").build();ContentProviderOperation op_email = ContentProviderOperation.newInsert(uri).withValueBackReference("raw_contact_id", 0).withValue("mimetype", "vnd.android.cursor.item/email_v2").withValue("data2", "2").withValue("data1", "aaa@163.com").build();  ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();operations.add(op_main);operations.add(op_name);operations.add(op_phone);operations.add(op_email);try {resolver.applyBatch("com.android.contacts", operations);} catch (Exception e) {e.printStackTrace();}
}

ContentObserver

使用说明

有时我们不但要获取以往的数据,还要实时获取新增的数据,最常见的业务场景便是短信验证码。电商APP中常常在用户注册或者付款时下发验证码短信,这时为提高用户体验,APP就得自动获取手机刚收到的短信验证码。类似的场景在系统APP中也存在,比如流量监控APP向运营商发送流量校准短信,此时APP也得自动拦截短信来获取流量信息。
由于系统在接收短信后会同时发出一个广播“android.provider.Telephony.SMS_RECEIVED”,所以我们可以使用广播接收器来监听短信的接收动作。然而不是所有的系统数据变更都会触发广播(比如添加联系人),所以Android又提供了ContentObserver类,该类可协助处理Content数据变化的监听事件。
下面是在ContentResolver对象中使用ContentObserver的相关方法:
registerContentObserver : 注册内容观察者。
unregisterContentObserver : 注销内容观察者。
notifyChange : 通知内容观察者发生了数据变化。

下面是两种监听方式在监听短信接收中的具体运用,监听结果消息使用了Notification推送到消息栏,有关Notification的使用说明参见《 Android开发笔记(五十二)通知推送Notification》。

广播方式

广播类的代码示例如下:

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.util.Log;public class SmsGetReceiver extends BroadcastReceiver {private static final String TAG = "SmsGetReceiver";@Overridepublic void onReceive(Context context, Intent intent) {Log.d(TAG, "onReceive");Bundle bundle = intent.getExtras();SmsMessage[] smsMessages = null;Object[] pdus = null;if (bundle != null) {pdus = (Object[]) bundle.get("pdus");}if (pdus !=null){smsMessages = new SmsMessage[pdus.length];String sender = "";String content = "";for (int i=0; i<pdus.length; i++){smsMessages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);sender = smsMessages[i].getOriginatingAddress();content = smsMessages[i].getMessageBody();Log.d(TAG, "SMS:"+sender+content);}NotificationUtil.sendSmsNotify(context, "广播来源:"+sender, content);}}
}

配置文件需要注册该广播

     <receiver android:name=".content.util.SmsGetReceiver"><intent-filter><action android:name="android.provider.Telephony.SMS_RECEIVED" /></intent-filter></receiver>

观察者方式

观察者类的代码示例如下:

import android.annotation.TargetApi;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.provider.Telephony;@TargetApi(Build.VERSION_CODES.KITKAT)
public class SmsGetObserver extends ContentObserver {private static final String TAG = "SmsGetObserver";private Context mContext;private static Uri mSmsUri;private static String[] mSmsColumn;public SmsGetObserver(Context context, Handler handler) {super(handler);mContext = context;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {mSmsUri = Telephony.Sms.Inbox.CONTENT_URI;mSmsColumn = new String[] { Telephony.Sms.ADDRESS, Telephony.Sms.BODY, Telephony.Sms.DATE };} else {mSmsUri = Uri.parse("content://sms/inbox");mSmsColumn = new String[] { "address","body","date" };}}@Overridepublic void onChange(boolean selfChange) {String sender = "";String content = "";String selection = String.format("date>%d", System.currentTimeMillis()-2*1000);Cursor cursor = mContext.getContentResolver().query(mSmsUri, mSmsColumn, selection, null, null);while(cursor.moveToNext()){sender = cursor.getString(0);content = cursor.getString(1);}cursor.close();NotificationUtil.sendSmsNotify(mContext, "观察者来源:"+sender, content);super.onChange(selfChange);}
}

主页面中对观察者类的调用代码如下:

     SmsGetObserver observer = new SmsGetObserver(this, new Handler());getContentResolver().registerContentObserver(Uri.parse("content://sms"), true, observer);

常用的Uri

总结下在Content组件中使用过程中遇到的几个Uri常量:
联系人信息(不包含手机号与电子邮箱):
ContactsContract.Contacts.CONTENT_URI   content://com.android.contacts/contacts
联系人电话信息:
ContactsContract.CommonDataKinds.Phone.CONTENT_URI   content://com.android.contacts/data/phones
联系人邮箱信息:
ContactsContract.CommonDataKinds.Email.CONTENT_URI   content://com.android.contacts/data/emails
SIM卡联系人信息:
content://icc/adn
短信信息:
Telephony.Sms.CONTENT_URI   content://sms
彩信信息:
Telephony.Mms.CONTENT_URI   content://mms
通话记录信息:
CallLog.Calls.CONTENT_URI   content://call_log/calls

下面是与短信有关的Uri分类说明:
收件箱:
Telephony.Sms.Inbox.CONTENT_URI   content://sms/inbox
已发送:
Telephony.Sms.Sent.CONTENT_URI   content://sms/sent
草稿箱:
Telephony.Sms.Draft.CONTENT_URI   content://sms/draft
发件箱(正在发送的信息):
Telephony.Sms.Outbox.CONTENT_URI   content://sms/outbox
发送失败:
content://sms/failed         
待发送列表(比如开启飞行模式后,该短信就在待发送列表里):
content://sms/queued

点此查看Android开发笔记的完整目录

Android开发笔记(五十四)数据共享接口ContentProvider相关推荐

  1. Android开发笔记(十四)圆弧进度动画CircleAnimation

    一个好看的APP,都有不少精致的动画效果.熟练运用各种动画技术,可让我们的APP灼灼生辉.Android在技术上把动画分为了三类,分别是帧动画FrameAnimation.补间动画TweenAnima ...

  2. Xamarin.Android开发实践(十四)

    原文:Xamarin.Android开发实践(十四) Xamarin.Android之ListView和Adapter 一.前言 如今不管任何应用都能够看到列表的存在,而本章我们将学习如何使用Xama ...

  3. Android开发问题集锦十四--绚丽的烟花

    Android开发问题集锦十四--绚丽的烟花 程序之美 前言 源码下载 程序之美 前言 随着一声突如其来的响声,打破了久违的不能喘息般的的寂静.一团彩色的光芒快速上升着,留下一线灰色的烟雾.啪!一朵& ...

  4. Qt笔记(五十四)之Activate控件开发

    一.Activate控件简介(内容摘自自百度) ActiveX控件是Microsoft的ActiveX技术的一部分.ActiveX控件是可以在应用程序和网络中计算机上重复使用的程序对象.创建它的主要技 ...

  5. Android开发笔记(十五)淡入淡出动画TransitionDrawable

    说到淡入淡出动画,可能大家会想到补间动画里面的AlphaAnimation,不过这个深浅动画只能对透明度做渐变效果,也就是只能对一个图形做深浅的颜色变换.如果我们想要从A图片逐渐变为B图片,也就是要实 ...

  6. Android开发笔记(九十四)图片的基本加工

    位图管理Bitmap Android上的图形使用Drawable类,而位图管理则使用Bitmap类,java上与之对应的是awt包中的BufferedImage.Android开发中有需要对jpg.p ...

  7. Android开发笔记(十八)书籍翻页动画PageAnimation

    前面几节的动画都算简单,本文就介绍一个复杂点的动画--书籍翻页动画.Android有自带的翻页动画ViewPager,不过ViewPager只实现了平移效果.即便使用补间组合动画或者属性动画,也只是把 ...

  8. Android开发笔记(十六)秋千摇摆动画SwingAnimation

    上节博主介绍了AlphaAnimation和淡入淡出动画的使用,其实AlphaAnimation只是四种补间动画中的一种.那么为了加深对其他补间动画的理解,我想说说旋转动画RotateAnimatio ...

  9. Android开发笔记(十九)底部标签栏TabBar

    底部标签页实现思路 现在的APP,大多在页面底部显示标签栏Tabbar,用于切换不同栏目的页面.Tabbar起源于iOS,iOS的Tabbar自动位于页面下方,可是Android搬过来的时候做了改动, ...

  10. Android开发笔记(十二)测量尺寸与下拉刷新

    尺寸测量的配置 控件宽和高的设置方式 大家知道,自定义视图的目的就是要在屏幕上显示期望的图案,那在绘制图案之前,我们得先知道这个图案的尺寸(如宽多少高多少). 一般在xml中给控件的宽和高有三种赋值方 ...

最新文章

  1. python cx oracle安装_python3.6的安装及cx_oracle安装
  2. css txt-aline,CSS规则定义.doc
  3. 任务队列,消息队列和rpc的区别是什么?
  4. Book Review 《构建之法》-2
  5. 将字符串的部分保存,剩余删去,或只保留指定一段子字符串
  6. 事物 @Transactional
  7. 夺命雷公狗---node.js---20之项目的构建在node+express+mongo的博客项目5mongodb在项目中实现添加数据...
  8. spring boot微服务通用部署启动脚本
  9. Linux——tar打包文件和压缩解压缩
  10. vector容器详细介绍
  11. ElasticSearch Client详解
  12. 主流手机user-agent与支持图像尺寸对照表(联通的)
  13. linux h5cc 编译,雷霆传奇H5源码编译+Linux+Release+Docker_2021/02/04
  14. native8081端口 react_教你轻松修改React Native端口(如何同时运行多个React Native、8081端口占用问题)...
  15. 433MHz资讯一点通
  16. 皮尔森 统计学相关性分析_统计学之三大相关性系数(pearson、spearman、kendall)...
  17. java实现lbs_如何在 Java 中利用 redis 实现 LBS 服务
  18. oracle修改表的owner,Oracle中改变表的Owner和tablespace
  19. Halcon算子:灰度共生矩阵gen_cooc_matrix、cooc_feature_matrix、cooc_feature_image
  20. 机器博弈:非零和博弈下的叶值表剪枝

热门文章

  1. scikit-klearn之 1.决策树
  2. windows下dlib库简介、安装问题解决及简单小例子 (python)
  3. Python爬虫实战01:Requests+正则表达式爬取猫眼电影
  4. 第二篇 Python数据类型、字符编码、文件处理
  5. linux中 字符串,linux内核驱动中对字符串的操作
  6. html 日志记录组件,使用HTML自定义格式的Log4j.properties进行日志记录
  7. java解三角函数方程_Java 中的三角函数
  8. SpringMVC jsp界面值渲染不出来
  9. 第2章[2.6] 组件与容器的选择与开发
  10. Java 中使用JDBC连接数据库例程与注意事项