android开发学习(四)—— 内容观察者,读写联系人


        当我们需要时刻查询某个数据库值是否发生了变化,如果开启一个线程对其时刻监测,就会导致的开销很大。这时我们需要内容观察者,来时刻检测内容是否发生变化。

则使用ContentObserver的情况主要有一下两者情况:

              1、需要频繁检测的数据库或者某个数据是否发生改变,如果使用线程去操作,很不经济而且很耗时 ;

              2、在用户不知晓的情况下对数据库做一些事件,比如:悄悄发送信息、拒绝接受短信黑名单等;


一、监测内容变化:

抽象类ContentResolver类中的方法原型如下:

    public final void registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer)

             功能:为指定的Uri注册一个ContentObserver派生类实例,当给定的Uri发生改变时,回调该实例对象去处理。

             参数:uri          需要观察的Uri(需要在UriMatcher里注册,否则该Uri也没有意义了)

                        notifyForDescendents  为false 表示精确匹配,即只匹配该Uri

                                                                     为true 表示可以同时匹配其派生的Uri

                        observer       ContentObserver的派生类实例

    public final void  unregisterContentObserver(ContentObserver observer)

          功能:取消对给定Uri的观察

          参数: observer     ContentObserver的派生类实例


这里是一个监测短信变化的案例:(运行这个软件时不要按返回键,那样会退出,应该按HOME键,使其在后台运行,这样就能检测其变化的内容)

<span style="font-size:14px;">package com.example.ContentView;

import android.app.Activity;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.widget.Toast;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		ContentResolver resolver = getContentResolver();
		Uri uri = Uri.parse("content://sms/");
		resolver.registerContentObserver(uri, true, new MyObserver(
				new Handler()));
	}

	private class MyObserver extends ContentObserver {

		public MyObserver(Handler handler) {
			super(handler);

		}

		// 当观察者观察到数据库的内容变化了,调用这个方法
		// 观察到消息邮箱里面有一条数据库内容变化的通知
		@Override
		public void onChange(boolean selfChange) {
			super.onChange(selfChange);
			Toast.makeText(MainActivity.this, "数据库的内容变化了", 1).show();
			ContentResolver resolver = getContentResolver();
			Uri uri = Uri.parse("content://sms/");
			Cursor cursor = resolver.query(uri, new String[] { "address",
					"type", "date", "body" }, null, null, null);
			cursor.moveToFirst();
			String address = cursor.getString(0);
			String body = cursor.getString(3);
			System.out.println(address + "------" + body);
			cursor.close();
		}

	}
}
</span>

运行结果:


二、读取联系人:

联系人的表比较复杂,所以想要获取其数据,可以先去将其数据库内容导出,看它有些什么内容。



可以发现联系人的数据库主要由三张基本表组成,分别是raw_contacts,data,mimetypes:



如图,读取所有的联系人的步骤已经列出来了

这需要注意的是,第二步和第三步的步骤是这样的,但是实际上不是依次读取data,mimetypes这两张表,而是读取view_data视图


<span style="font-size:14px;">package com.example.ReadContacts;

import android.app.Activity;
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}

	public void click(View view) {
		ContentResolver resolver = getContentResolver();
		//获取raw_contacts表对应的uri
		Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
		Uri dateUri = Uri.parse("content://com.android.contacts/data");
		Cursor cursor = resolver.query(uri, null, null, null, null);
		while (cursor.moveToNext()) {
			// 1.查询raw_contacts表,获取联系人的id
			String id = cursor.getString(cursor.getColumnIndex("contact_id"));
			if (id != null) {
				//2.根据id查询data表,获取id对应联系人的数据
				Cursor dataCursor = resolver.query(dateUri, null,
						"raw_contact_id = ?", new String[] { id }, null);
				while (dataCursor.moveToNext()) {
					String data1 = dataCursor.getString(dataCursor
							.getColumnIndex("data1"));
					//这里不能填写mimetype_id,因为读的是
					String mimetype = dataCursor.getString(dataCursor
							.getColumnIndex("mimetype"));
					System.out.println("data1:" + data1);
					System.out.println("mimetype:" + mimetype);
				}
				dataCursor.close();
				System.out.println("--------------");
			}
		}
		cursor.close();
	}
}
</span>

这里的布局代码和权限添加省略,记得就行。

运行结果是:

如果把mimetype改为mimetype_id,logcat会显示:




这里另外谈一个细节问题:倘若把上面的代码中的 if (id != null) 判断删去,然后删去一条联系人再执行程序读取联系人记录,这时就会发生错误:


错误显示的是第一行为null,读取不出联系人,而我们在通讯录中看到的记录却有可见的第一个联系人。

注意,这里说的是可见的联系人,既然这么说,那么就说明还有通讯录上看不到的联系人。

没错。

我把通讯录中的第一个人删去,而在系统中实际上并未删去,而是将这个联系人的contact_id设置为null,这样通讯录就读不出联系人了,这个联系人也就从通讯录中消失了:


如图所示的contact_id的被设置为null,就是被删去的记录,虽然删去了,但是在联系人的数据库中仍然存在。

Google工程师这样设计的原因是每个人的android都可以注册一个Google的帐号,这样通讯录就会云备份到云存储上,可是怎么更新云备份呢?如果我们直接将数据从数据库中删去,那么云备份就是重新遍历一次,把所有的联系人重新备份一次,这样工程量就比较大,效率也很低。所以Google工程师就把通讯录每个联系人设置一个contact_id,如果删去,就将其设置为null,这样同步备份的时候,只需要遍历contact_id,只将null值的联系人从云端删去,这样就提高的效率。

所以在读取联系人的时候,需要加一个限定的条件,将null的联系人排除出去,不用读它。


三、保存联系人到系统通讯录

类似写一条短信到系统,将联系人写入联系人通讯录,都可以实现将备份的记录恢复到系统。

这里的案例只是简单的将联系人写入通讯录,使用到内容提供者的插入方法:

具体的步骤可以查看二中的第二图,写联系人。

package com.example.writeContact;

import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}

	public void click(View view) {
		ContentResolver resolver = getContentResolver();
		Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
		Uri dateUri = Uri.parse("content://com.android.contacts/data");
		
		ContentValues values = new ContentValues();
		
		// 获取最后一个人的ID是多少
		Cursor cursor = resolver.query(uri, new String[] { "_id" }, null, null,
				null);
		cursor.moveToLast();
		int lastId = cursor.getInt(0);
		int newId = lastId + 1;
		//1.在raw_contacts表中添加一个新的id
		values.put("_id", newId);
		resolver.insert(uri, values);

		// 2.使用id向data表中添加数据
		// 添加电话
		ContentValues phoneValue = new ContentValues();
		phoneValue.put("data1", "88888888");
		phoneValue.put("mimetype", "vnd.android.cursor.item/phone_v2");
		phoneValue.put("raw_contact_id", newId);
		resolver.insert(dateUri, phoneValue);

		// 添加邮件
		ContentValues emailValue = new ContentValues();
		emailValue.put("data1", "qiudong@gmail.com");
		emailValue.put("mimetype", "vnd.android.cursor.item/email_v2");
		emailValue.put("raw_contact_id", newId);
		resolver.insert(dateUri, emailValue);

		// 添加名字
		ContentValues nameValue = new ContentValues();
		nameValue.put("data1", "leifeng");
		nameValue.put("mimetype", "vnd.android.cursor.item/name");
		nameValue.put("raw_contact_id", newId);
		resolver.insert(dateUri, nameValue);

		Toast.makeText(this, "添加成功", 0).show();
	}
}

记得添加权限:





  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值