前言: 工作中,需要开启一个线程大量的查询某个数据库值发送了变化,导致的开销很大,后来在老大的指点下,利用了
???? ContentObserver完美的解决了该问题,感到很兴奋,做完之后自己也对ContentObserver做下总结。
?
????????? ContentObserver——内容观察者,目的是观察(捕捉)特定Uri引起的数据库的变化,继而做一些相应的处理,它类似于
?? 数据库技术中的触发器(Trigger),当ContentObserver所观察的Uri发生变化时,便会触发它。触发器分为表触发器、行触发器,
? 相应地ContentObserver也分为“表“ContentObserver、“行”ContentObserver,当然这是与它所监听的Uri MIME Type有关的。
?
????? ???? 熟悉Content Provider(内容提供者)的应该知道,我们可以通过UriMatcher类注册不同类型的Uri,我们可以通过这些不同的
??? Uri来查询不同的结果。根据Uri返回的结果,Uri Type可以分为:返回多条数据的Uri、返回单条数据的Uri。
?
?
??注册/取消注册ContentObserver方法,抽象类ContentResolver类中的方法原型如下:
?
????public final void??registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer)
???????????? 功能:为指定的Uri注册一个ContentObserver派生类实例,当给定的Uri发生改变时,回调该实例对象去处理。
?????????? ??参数:uri??????????需要观察的Uri(需要在UriMatcher里注册,否则该Uri也没有意义了)
??????????????????? ????notifyForDescendents??为false 表示精确匹配,即只匹配该Uri
?????????????????????????????????????????????????????? ????????? ??? 为true 表示可以同时匹配其派生的Uri,举例如下:
??????????????????? ????? 假设UriMatcher 里注册的Uri共有一下类型:
??????????????????????????????? 1 、content://com.qin.cb/student (学生)
??????????????????????????????? 2 、content://com.qin.cb/student/#?
?????????????????????????????? ?3、?content://com.qin.cb/student/schoolchild(小学生,派生的Uri)
?
????????????????????假设我们当前需要观察的Uri为content://com.qin.cb/student,如果发生数据变化的?Uri?为???
?????????? content://com.qin.cb/student/schoolchild ,当notifyForDescendents为 false,那么该ContentObserver会监听不到,??
????????? ?但是当notifyForDescendents 为ture,能捕捉该Uri的数据库变化。
?
???????????????????? observer????? ?ContentObserver的派生类实例
?
?
????public final void??unregisterContentObserver(ContentObserver?observer)
????????? 功能:取消对给定Uri的观察
????????? 参数: observer ContentObserver的派生类实例
?
????????
ContentObserver类介绍
?
??构造方法?public void?ContentObserver(Handler?handler)??
?????????????????????? 说明:所有?? ContentObserver的派生类都需要调用该构造方法
? 参数: handler Handler对象。可以是主线程Handler(这时候可以更新UI 了),也可以是任何Handler对象。
?常用方法
?? void?onChange(boolean selfChange)
????? ?功能:当观察到的Uri发生变化时,回调该方法去处理。所有ContentObserver的派生类都需要重载该方法去处理逻辑。
???? ??参数:selfChange 回调后,其值一般为false,该参数意义不大(我也不懂,理解方法最重要)。
?
? 另外两个方法,用处不大,我也不懂,大家参照SDK自行理解,冒昧了。
? boolean??deliverSelfNotifications()
??? ?说明:Returns true if this observer is interested in notifications for changes made through the cursor the observer is registered with.
??
? final void?dispatchChange(boolean selfChange)
?
?
??观察特定Uri的步骤如下:
?
??? ?1、??? 创建我们特定的ContentObserver派生类,必须重载父类构造方法,必须重载onChange()方法去处理回调后的功能实现
???? 2、??? 利用context.getContentResolover()获得ContentResolove对象,接着调用registerContentObserver()方法去注册内容观察者
???? 3、??? 由于ContentObserver的生命周期不同步于Activity和Service等,因此,在不需要时,需要手动的调用
?????????????unregisterContentObserver()去取消注册。 ???
?
?
?
好了,基本讲解就介绍到这儿了。下面给出小DEMO的简单说明:
???? Demo中共有两个不同的ContentObserver派生类,如下:
??????? 1、用来观察系统是否改变了飞行模式状态,
????????? PS: 大家可以去SDK中查看该类:android.provider.Settings.System。该类封装了对设置模块下所有值的存取,比如:
???????? ?飞行模式状态、蓝牙状态、屏幕亮度值等,并且提供了相应的Uri。
????????2、观察系统的短信息数据发生了变化。当监听到短信数据发生变化时,查询所有已发送的短信并且显示出来。
?
?? 短信的Uri共有一下几种:
?????????????????????????content://sms/inbox? ???收件箱 ????????
?????????????????????????content://sms/sent????????已发送?
?????????????????????????content://sms/draft????? ??草稿 ??????????
???????????????????????? content://sms/outbox????发件箱?? ??????? (正在发送的信息)
?????????????????????????content://sms/failed??????发送失败?????
?????????????????????????content://sms/queued??待发送列表? (比如开启飞行模式后,该短信就在待发送列表里)
?
关于短信的更多内容可以参考该博客:<android 中管理短信>
?
????? 当开启飞行模式和发送短信后(注意:使用Home键退出,而不是Back键),DMEO截图如下:
?
???????????????
?
DEMO文件如下:
1、 观察飞行模式状态的ContentObserver派生类,AirplaneContentObserver.java
?
- package?com.qin.contentobserver;??
- ??
- import?android.content.Context;??
- import?android.database.ContentObserver;??
- import?android.net.Uri;??
- import?android.os.Handler;??
- import?android.provider.*;??
- import?android.provider.Settings.SettingNotFoundException;??
- import?android.util.Log;??
- ??
- ??
- //用来观察system表里飞行模式所在行是否发生变化?,?“行”内容观察者??
- public?class?AirplaneContentObserver?extends?ContentObserver?{??
- ??
- ????private?static?String?TAG?=?"AirplaneContentObserver"?;??
- ??????
- ????private?static?int?MSG_AIRPLANE?=?1?;??
- ??????
- ????private?Context?mContext;??????
- ????private?Handler?mHandler?;??//此Handler用来更新UI线程??
- ??????
- ????public?AirplaneContentObserver(Context?context,?Handler?handler)?{??
- ????????super(handler);??
- ????????mContext?=?context;??
- ????????mHandler?=?handler?;??
- ????}??
- ??
- ????/**?
- ?????*?当所监听的Uri发生改变时,就会回调此方法?
- ?????*??
- [email protected]下该回调值false?
- ?????*/??
- ????@Override??
- ????public?void?onChange(boolean?selfChange)?{??
- ????????Log.i(TAG,?"-------------the?airplane?mode?has?changed-------------");??
- ??????????
- ????????//?系统是否处于飞行模式下??
- ????????try?{??
- ????????????int?isAirplaneOpen?=?Settings.System.getInt(mContext.getContentResolver(),?Settings.System.AIRPLANE_MODE_ON);??
- ????????????Log.i(TAG,?"?isAirplaneOpen?----->?"?+isAirplaneOpen)?;??
- ????????????mHandler.obtainMessage(MSG_AIRPLANE,isAirplaneOpen).sendToTarget()?;??
- ????????}??
- ????????catch?(SettingNotFoundException?e)?{??
- ????????????//?TODO?Auto-generated?catch?block??
- ????????????e.printStackTrace();??
- ????????}??
- ??
- ????}??
- ??
- }??
?
????????
2、观察系统里短消息的数据库变化的ContentObserver派生类,SMSContentObserver.java
??
- package?com.qin.contentobserver;??
- ??
- import?android.content.Context;??
- import?android.database.ContentObserver;??
- import?android.database.Cursor;??
- import?android.net.Uri;??
- import?android.os.Handler;??
- import?android.util.Log;??
- ??
- ??
- //用来观察系统里短消息的数据库变化??”表“内容观察者,只要信息数据库发生变化,都会触发该ContentObserver?派生类??
- public?class?SMSContentObserver?extends?ContentObserver?{??
- ????private?static?String?TAG?=?"SMSContentObserver";??
- ??????
- ????private?int?MSG_OUTBOXCONTENT?=?2?;??
- ??????
- ????private?Context?mContext??;??
- ????private?Handler?mHandler?;???//更新UI线程??
- ??????
- ????public?SMSContentObserver(Context?context,Handler?handler)?{??
- ????????super(handler);??
- ????????mContext?=?context?;??
- ????????mHandler?=?handler?;??
- ????}??
- ????/**?
- ?????*?当所监听的Uri发生改变时,就会回调此方法?
- ?????*??
- [email protected]??下该回调值false?
- ?????*/??
- ????@Override??
- ????public?void?onChange(boolean?selfChange){??
- ????????Log.i(TAG,?"the?sms?table?has?changed");??
- ??????????
- ????????//查询发件箱里的内容???????
- ????????Uri?outSMSUri?=?Uri.parse("content://sms/sent")?;??
- ??????????
- ????????Cursor?c?=?mContext.getContentResolver().query(outSMSUri,?null,?null,?null,"date?desc");??
- ????????if(c?!=?null){??
- ??????????????
- ????????????Log.i(TAG,?"the?number?of?send?is"+c.getCount())?;??
- ??????????????
- ????????????StringBuilder?sb?=?new?StringBuilder()?;??
- ????????????//循环遍历??
- ????????????while(c.moveToNext()){??
- //??????????????sb.append("发件人手机号码:?"+c.getInt(c.getColumnIndex("address")))??
- //????????????????.append("信息内容:?"+c.getInt(c.getColumnIndex("body")))??
- //????????????????.append("是否查看:?"+c.getInt(c.getColumnIndex("read")))???
- //????????????????.append("发送时间:?"+c.getInt(c.getColumnIndex("date")))??
- //????????????????.append("\n");??
- ????????????????sb.append("发件人手机号码:?"+c.getInt(c.getColumnIndex("address")))??
- ??????????????????.append("信息内容:?"+c.getString(c.getColumnIndex("body")))??
- ??????????????????.append("\n");??
- ????????????}??
- ????????????c.close();????????????
- ????????????mHandler.obtainMessage(MSG_OUTBOXCONTENT,?sb.toString()).sendToTarget();??????????
- ????????}??
- ????}??
- ??????
- }??
?
?3、主工程逻辑为MainActivity.java,对短消息的观察Uri,通过测试我发现只能监听此Uri “content://sms” (等同于"content://sms/"),而不能监听其他的Uri,比如"content://sms/outbox"等。
?
- package?com.qin.contentobserver;??
- ??
- import?android.app.Activity;??
- import?android.database.Cursor;??
- import?android.net.Uri;??
- import?android.os.Bundle;??
- import?android.os.Handler;??
- import?android.os.Message;??
- import?android.provider.*;??
- import?android.util.Log;??
- import?android.widget.EditText;??
- import?android.widget.TextView;??
- ??
- public?class?MainActivity?extends?Activity?{??
- ??
- ????private?TextView?tvAirplane;??
- ????private?EditText?etSmsoutbox;??
- ??
- ????//?Message?类型值??
- ????private?static?final?int?MSG_AIRPLANE?=?1;??
- ????private?static?final?int?MSG_OUTBOXCONTENT?=?2;??
- ??
- ????private?AirplaneContentObserver?airplaneCO;??
- ????private?SMSContentObserver?smsContentObserver;??
- ??
- ????/**?Called?when?the?activity?is?first?created.?*/??
- ????@Override??
- ????public?void?onCreate(Bundle?savedInstanceState)?{??
- ????????super.onCreate(savedInstanceState);??
- ????????setContentView(R.layout.main);??
- ??
- ????????tvAirplane?=?(TextView)?findViewById(R.id.tvAirplane);??
- ????????etSmsoutbox?=?(EditText)?findViewById(R.id.smsoutboxContent);??
- ??
- ????????//?创建两个对象??
- ????????airplaneCO?=?new?AirplaneContentObserver(this,?mHandler);??
- ????????smsContentObserver?=?new?SMSContentObserver(this,?mHandler);??
- ??????????
- ????????//注册内容观察者??
- ????????registerContentObservers()?;??
- ????}??
- ??
- ????private?void?registerContentObservers()?{??
- ????????//?通过调用getUriFor?方法获得?system表里的"飞行模式"所在行的Uri??
- ????????Uri?airplaneUri?=?Settings.System.getUriFor(Settings.System.AIRPLANE_MODE_ON);??
- ????????//?注册内容观察者??
- ????????getContentResolver().registerContentObserver(airplaneUri,?false,?airplaneCO);??
- ????????//?”表“内容观察者?,通过测试我发现只能监听此Uri?----->?content://sms??
- ????????//?监听不到其他的Uri?比如说?content://sms/outbox??
- ????????Uri?smsUri?=?Uri.parse("content://sms");??
- ????????getContentResolver().registerContentObserver(smsUri,?true,smsContentObserver);??
- ????}??
- ??
- ????private?Handler?mHandler?=?new?Handler()?{??
- ??
- ????????public?void?handleMessage(Message?msg)?{??
- ??????????????
- ????????????System.out.println("---mHanlder----");??
- ????????????switch?(msg.what)?{??
- ????????????case?MSG_AIRPLANE:??
- ????????????????int?isAirplaneOpen?=?(Integer)?msg.obj;??
- ????????????????if?(isAirplaneOpen?!=?0)??
- ????????????????????tvAirplane.setText("飞行模式已打开");??
- ????????????????else?if?(isAirplaneOpen?==?0)??
- ????????????????????tvAirplane.setText("飞行模式已关闭");??
- ????????????????break;??
- ????????????case?MSG_OUTBOXCONTENT:??
- ????????????????String?outbox?=?(String)?msg.obj;??
- ????????????????etSmsoutbox.setText(outbox);??
- ????????????????break;??
- ????????????default:??
- ????????????????break;??
- ????????????}??
- ????????}??
- ????};??
- }??
?
? 在此基础上,你可以利用ContentObserver去实现短信黑名单以及悄悄发送短信等技巧,具体可以参考这篇博客:
????????????????< 接受指定号码的短信>
?
?总结: 使用ContentObserver的情况主要有一下两者情况:
???????? ??? ?1、需要频繁检测的数据库或者某个数据是否发生改变,如果使用线程去操作,很不经济而且很耗时 ;
???????????? ?2、在用户不知晓的情况下对数据库做一些事件,比如:悄悄发送信息、拒绝接受短信黑名单等;
?
? 在这两种情形下,使用ContentObserver无疑是最好的利刃了。
?
?? ?代码下载地址为:http://download.csdn.net/detail/qinjuning/3896987
?
?
原文地址:http://blog.csdn.net/qinjuning/article/details/7047607