转载请注明出处:http://blog.csdn.net/qinjuning

前言: 工作中,需要开启一个线程大量的查询某个数据库值发送了变化,导致的开销很大,后来在老大的指点下,利用了

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 voidunregisterContentObserver(ContentObserver observer)

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

参数: observer ContentObserver的派生类实例

ContentObserver类介绍

构造方法 public voidContentObserver(Handler handler)

说明:所有 ContentObserver的派生类都需要调用该构造方法

       参数: handler  Handler对象。可以是主线程Handler(这时候可以更新UI 了),也可以是任何Handler对象。

常用方法

voidonChange(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

[java] view plain copy print ?
  1. packagecom.qin.contentobserver;
  2. importandroid.content.Context;
  3. importandroid.database.ContentObserver;
  4. importandroid.net.Uri;
  5. importandroid.os.Handler;
  6. importandroid.provider.*;
  7. importandroid.provider.Settings.SettingNotFoundException;
  8. importandroid.util.Log;
  9. //用来观察system表里飞行模式所在行是否发生变化,“行”内容观察者
  10. publicclassAirplaneContentObserverextendsContentObserver{
  11. privatestaticStringTAG="AirplaneContentObserver";
  12. privatestaticintMSG_AIRPLANE=1;
  13. privateContextmContext;
  14. privateHandlermHandler;//此Handler用来更新UI线程
  15. publicAirplaneContentObserver(Contextcontext,Handlerhandler){
  16. super(handler);
  17. mContext=context;
  18. mHandler=handler;
  19. }
  20. /**
  21. *当所监听的Uri发生改变时,就会回调此方法
  22. *
  23. *@paramselfChange此值意义不大一般情况下该回调值false
  24. */
  25. @Override
  26. publicvoidonChange(booleanselfChange){
  27. Log.i(TAG,"-------------theairplanemodehaschanged-------------");
  28. //系统是否处于飞行模式下
  29. try{
  30. intisAirplaneOpen=Settings.System.getInt(mContext.getContentResolver(),Settings.System.AIRPLANE_MODE_ON);
  31. Log.i(TAG,"isAirplaneOpen----->"+isAirplaneOpen);
  32. mHandler.obtainMessage(MSG_AIRPLANE,isAirplaneOpen).sendToTarget();
  33. }
  34. catch(SettingNotFoundExceptione){
  35. //TODOAuto-generatedcatchblock
  36. e.printStackTrace();
  37. }
  38. }
  39. }
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发生改变时,就会回调此方法 * * @param selfChange 此值意义不大 一般情况下该回调值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

[java] view plain copy print ?
  1. packagecom.qin.contentobserver;
  2. importandroid.content.Context;
  3. importandroid.database.ContentObserver;
  4. importandroid.database.Cursor;
  5. importandroid.net.Uri;
  6. importandroid.os.Handler;
  7. importandroid.util.Log;
  8. //用来观察系统里短消息的数据库变化”表“内容观察者,只要信息数据库发生变化,都会触发该ContentObserver派生类
  9. publicclassSMSContentObserverextendsContentObserver{
  10. privatestaticStringTAG="SMSContentObserver";
  11. privateintMSG_OUTBOXCONTENT=2;
  12. privateContextmContext;
  13. privateHandlermHandler;//更新UI线程
  14. publicSMSContentObserver(Contextcontext,Handlerhandler){
  15. super(handler);
  16. mContext=context;
  17. mHandler=handler;
  18. }
  19. /**
  20. *当所监听的Uri发生改变时,就会回调此方法
  21. *
  22. *@paramselfChange此值意义不大一般情况下该回调值false
  23. */
  24. @Override
  25. publicvoidonChange(booleanselfChange){
  26. Log.i(TAG,"thesmstablehaschanged");
  27. //查询发件箱里的内容
  28. UrioutSMSUri=Uri.parse("content://sms/sent");
  29. Cursorc=mContext.getContentResolver().query(outSMSUri,null,null,null,"datedesc");
  30. if(c!=null){
  31. Log.i(TAG,"thenumberofsendis"+c.getCount());
  32. StringBuildersb=newStringBuilder();
  33. //循环遍历
  34. while(c.moveToNext()){
  35. //sb.append("发件人手机号码:"+c.getInt(c.getColumnIndex("address")))
  36. //.append("信息内容:"+c.getInt(c.getColumnIndex("body")))
  37. //.append("是否查看:"+c.getInt(c.getColumnIndex("read")))
  38. //.append("发送时间:"+c.getInt(c.getColumnIndex("date")))
  39. //.append("\n");
  40. sb.append("发件人手机号码:"+c.getInt(c.getColumnIndex("address")))
  41. .append("信息内容:"+c.getString(c.getColumnIndex("body")))
  42. .append("\n");
  43. }
  44. c.close();
  45. mHandler.obtainMessage(MSG_OUTBOXCONTENT,sb.toString()).sendToTarget();
  46. }
  47. }
  48. }
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发生改变时,就会回调此方法 * * @param selfChange 此值意义不大 一般情况下该回调值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"等。

[java] view plain copy print ?
  1. packagecom.qin.contentobserver;
  2. importandroid.app.Activity;
  3. importandroid.database.Cursor;
  4. importandroid.net.Uri;
  5. importandroid.os.Bundle;
  6. importandroid.os.Handler;
  7. importandroid.os.Message;
  8. importandroid.provider.*;
  9. importandroid.util.Log;
  10. importandroid.widget.EditText;
  11. importandroid.widget.TextView;
  12. publicclassMainActivityextendsActivity{
  13. privateTextViewtvAirplane;
  14. privateEditTextetSmsoutbox;
  15. //Message类型值
  16. privatestaticfinalintMSG_AIRPLANE=1;
  17. privatestaticfinalintMSG_OUTBOXCONTENT=2;
  18. privateAirplaneContentObserverairplaneCO;
  19. privateSMSContentObserversmsContentObserver;
  20. /**Calledwhentheactivityisfirstcreated.*/
  21. @Override
  22. publicvoidonCreate(BundlesavedInstanceState){
  23. super.onCreate(savedInstanceState);
  24. setContentView(R.layout.main);
  25. tvAirplane=(TextView)findViewById(R.id.tvAirplane);
  26. etSmsoutbox=(EditText)findViewById(R.id.smsoutboxContent);
  27. //创建两个对象
  28. airplaneCO=newAirplaneContentObserver(this,mHandler);
  29. smsContentObserver=newSMSContentObserver(this,mHandler);
  30. //注册内容观察者
  31. registerContentObservers();
  32. }
  33. privatevoidregisterContentObservers(){
  34. //通过调用getUriFor方法获得system表里的"飞行模式"所在行的Uri
  35. UriairplaneUri=Settings.System.getUriFor(Settings.System.AIRPLANE_MODE_ON);
  36. //注册内容观察者
  37. getContentResolver().registerContentObserver(airplaneUri,false,airplaneCO);
  38. //”表“内容观察者,通过测试我发现只能监听此Uri----->content://sms
  39. //监听不到其他的Uri比如说content://sms/outbox
  40. UrismsUri=Uri.parse("content://sms");
  41. getContentResolver().registerContentObserver(smsUri,true,smsContentObserver);
  42. }
  43. privateHandlermHandler=newHandler(){
  44. publicvoidhandleMessage(Messagemsg){
  45. System.out.println("---mHanlder----");
  46. switch(msg.what){
  47. caseMSG_AIRPLANE:
  48. intisAirplaneOpen=(Integer)msg.obj;
  49. if(isAirplaneOpen!=0)
  50. tvAirplane.setText("飞行模式已打开");
  51. elseif(isAirplaneOpen==0)
  52. tvAirplane.setText("飞行模式已关闭");
  53. break;
  54. caseMSG_OUTBOXCONTENT:
  55. Stringoutbox=(String)msg.obj;
  56. etSmsoutbox.setText(outbox);
  57. break;
  58. default:
  59. break;
  60. }
  61. }
  62. };
  63. }
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

更多相关文章

  1. Android保持屏幕常亮的方法
  2. android 2.2+ 完全退出程序的方法今天贡献了
  3. Android启动和关闭Activity
  4. android OS Service
  5. Android在onCreate()中获得控件尺寸
  6. Android保持屏幕常亮的方法总结
  7. android 获取IP
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. android shape详解
  2. Android(安卓)代码混淆 防止反编译
  3. JS判断客户端是否是iOS或者Android手机移
  4. Android(安卓)SAX解析xml文件
  5. Android(安卓)startActivityForResult的
  6. (转)Android_GPS
  7. Android—自动弹出软键盘
  8. Android向通讯录添加联系人的一般方法
  9. Android(安卓)对话框【Dialog】去除白色
  10. 【Android】Android实现截取当前屏幕图片