前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android开发笔记(五十四)数据共享接口ContentProvider

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

作者头像
aqi00
发布2019-01-18 11:09:22
1.5K0
发布2019-01-18 11:09:22
举报
文章被收录于专栏:老欧说安卓

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在查看通讯信息中的具体运用:

读取联系人

代码示例如下:

代码语言:javascript
复制
	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”。

读取短信

代码示例如下:

代码语言:javascript
复制
	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();
	}

读取彩信

代码示例如下:

代码语言:javascript
复制
	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();
	}

读取通话记录

代码示例如下:

代码语言:javascript
复制
	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方式

代码示例如下:

代码语言:javascript
复制
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方式

代码示例如下:

代码语言:javascript
复制
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》。

广播方式

广播类的代码示例如下:

代码语言:javascript
复制
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";

    @Override
    public 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);
        }
    }
}

配置文件需要注册该广播

代码语言:javascript
复制
		<receiver android:name=".content.util.SmsGetReceiver">
				<intent-filter>
						<action android:name="android.provider.Telephony.SMS_RECEIVED" />
				</intent-filter>
		</receiver>

观察者方式

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

代码语言:javascript
复制
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" };
		}
	}

	@Override
	public 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);
	}
}

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

代码语言:javascript
复制
		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开发笔记的完整目录

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016年01月20日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ContentProvider
  • ContentResolver
    • 使用说明
      • 读取联系人
        • 读取短信
          • 读取彩信
            • 读取通话记录
            • ContentProviderOperation
              • 使用说明
                • ContentResolver方式
                  • ContentProviderOperation方式
                  • ContentObserver
                    • 使用说明
                      • 广播方式
                        • 观察者方式
                        • 常用的Uri
                        相关产品与服务
                        短信
                        腾讯云短信(Short Message Service,SMS)可为广大企业级用户提供稳定可靠,安全合规的短信触达服务。用户可快速接入,调用 API / SDK 或者通过控制台即可发送,支持发送验证码、通知类短信和营销短信。国内验证短信秒级触达,99%到达率;国际/港澳台短信覆盖全球200+国家/地区,全球多服务站点,稳定可靠。
                        领券
                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档