zoukankan      html  css  js  c++  java
  • 转载使用 ContentObsever 拦截短信,获取短信内容

    在一些应用上,比如手机银行,QQ,微信等,很多时候我们都需要通过发送验证码到手机上,然后把验证码填上去,然后才能成功地继续去做下面一步事情。

    而如果每次我们都要离开当前界面,然后去查收短信,记住验证码,然后再回来输入到控件中,这感觉就会很麻烦,用户体验就会很差,而像微信等一些应用,则会在手机接到短信 后,将信息中的验证码给抽出来,帮我们将其填到对应的控件中,多方便,对吧。

    这个功能就是通过ContentObserver来实现的。顾名思义,Content Observer,就是内容监听,它实现的功能就是对特定的Uri进行监听,当监听的Uri发生变化时,就能够根据开发者的意思去做相应的处理。

    下面我们就利用一个小小的Demo来简单地看一下,Content Observer是如何应用的。

    这个小Demo的功能就是会监听短信的到达,然后将短消息里面的内容放到一个TextView上面展示,具体效果如下面的GIF图片:

     

    具体的步骤如下:

    1)自定义一个类,它要继承ContentObserver类,并且实现其onChange方法:

    public class SMSContentObserver extends ContentObserver{
    
      private static final String TAG = "com.lms.codemo.SMSContentObserver";
      
      private Handler mHandler;
      
      private Context mContext;
      
      private ContentResolver mContentResolver;
      
      private Uri uri = Uri.parse("content://sms/inbox");
      
      private int mMsgCode;
      
      public SMSContentObserver(Handler handler) {
        super(handler);
        this.mHandler = handler;
      }
      
      public SMSContentObserver(Handler handler, Context context, int msgCode){
        super(handler);
        this.mHandler = handler;
        this.mContext = context;
        this.mMsgCode = msgCode;
        mContentResolver = mContext.getContentResolver();		
      }
      
        public void onChange(boolean selfChange) {
      super.onChange(selfChange);
      String[] projection = new String[] {"_id","address","body","type"};
      Cursor cursor = mContentResolver.query(uri, projection, null, null, "date desc");
      
      if(cursor == null){
        
      }else if(!cursor.moveToFirst()){
        
      }else{
        do{
          String msgBody = cursor.getString(cursor.getColumnIndex("body"));
          int msgId = cursor.getInt(cursor.getColumnIndex("_id"));
          String msgAddr  = cursor.getString(cursor.getColumnIndex("address"));
          String msgType  = cursor.getString(cursor.getColumnIndex("type"));
          Log.v(TAG, "msgId : " + msgId);
          Log.v(TAG, "msgAddr : " + msgAddr);
          Log.v(TAG, "msgBody : " + msgBody);
          Log.v(TAG, "msgType : " + msgType);
          Message message = Message.obtain();
          message.what = mMsgCode;
          message.obj = msgBody;
          mHandler.sendMessage(message);
          break;
        }while(cursor.moveToNext());
        cursor.close();
      }	
      
        }
    
    }

    这个SMSContentObserver继承了ContentObserver,并且定义了一个包含context,handler和msgCode的构造函数,其中

    1.1)context是为获得ContentResolver来查询系统短消息的Uri,

    1.2)handler是为了将获取到的内容发送回UI线程,进行UI的更新,

    1.3)msgCode是handler处理的消息代码,表明是处理短消息的消息。

    ContentObserver的原理其实就在于监听指定Uri的变化,在这个类中,因为我们是要获取 收到的 短消息的内容,所以在这里要去查询短消息收件箱的内容,其URI定义如下:

    private Uri uri = Uri.parse("content://sms/inbox");

    当指定的Uri变化了, ContentObserver就会调用其onChange函数,我们可以看到在ContentObserver类中的关于onChange函数的定义:

    /**
      * This method is called when a content change occurs.
      * <p>
      * Subclasses should override this method to handle content changes.
      * </p>
      *
      * @param selfChange True if this is a self-change notification.
      */
        public void onChange(boolean selfChange) {
         // Do nothing.  Subclass should override.
        }

    在这里指出,这个方法就是在内容改变的时候会被调用,同时要求这个函数必须由子类来实现,因为具体的逻辑是我们自己来定义的。

    在Onchange函数中,我们会通过context获得ContentResolver,按照日期的返序获得第一条短消息,然后通过Handler发送到UI主线程。真正在实际上应用的逻辑会更复杂,而不是单纯地将内容返回去,我们可以看到短消息的有几个主要的字段:

    a)type,表明是发送(2)还是接收(1)

    b)address,对应的号码

    c)body,对应的消息内容

    d)date,对应的日期

    等等。

    2)我们要将这个ConentObserver注册到对应的Context中,就跟注册广播一样啊,要在某个地方告诉系统,如果短消息内容变化了,你要告诉我一下,我好处理事情,对吧。

    这个小demo里面,就是我们的主界面,MainActivity了,代码如下:

    public class MainActivity extends Activity{
      
      private static final String TAG = "com.lms.codemo.MainActivity";
    
      private TextView tvShowMsg;
      
      private SMSContentObserver smsContentObserver;
      
      private static final int MSG_CODE = 1;
      
      private Handler mHandler = new Handler(){
        public void handleMessage(Message msg){
          if( MSG_CODE == msg.what){				
            String msgBody = (String) msg.obj;
            Log.v(TAG, "msg_body: " + msgBody);
            tvShowMsg.setText(msgBody);
          }
        }
      };
      
      protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        
        setContentView(R.layout.activity_main);
        tvShowMsg = (TextView)findViewById(R.id.tvShowMsg);
        
        smsContentObserver = new SMSContentObserver(mHandler,this,MSG_CODE);
        getContentResolver().registerContentObserver(Uri.parse("content://sms"), true, smsContentObserver);
            
      }
      
      protected void onDestroy(){
        super.onDestroy();
        getContentResolver().unregisterContentObserver(smsContentObserver);;
      }

    2.1)在Activity中,我们主要就是创建了一个handler对象,将其还有context传给SMSContentObserver,这样,它才能将消息给传回来,在主线程中处理,处理很简单,将返回来的消息内容直接更新到TextView上面。

    2.2)在Activity创建的时候,我们要创建实例化一个SMSContentObserver,然后调用ContentResolver的registerContentObserver方法,将指定的Uri绑定给它,这样,SMSContentObserver才知道它是要去监听这个Uri的内容,而在这里,当然就是监听短消息的内容了,如下:

    Uri.parse("content://sms")

    2.3)我们看一下这个函数的定义:

    /**
      * Register an observer class that gets callbacks when data identified by a
      * given content URI changes.
      *
      * @param uri The URI to watch for changes. This can be a specific row URI, or a base URI
      * for a whole class of content.
      * @param notifyForDescendents If <code>true</code> changes to URIs beginning with <code>uri</code>
      * will also cause notifications to be sent. If <code>false</code> only changes to the exact URI
      * specified by <em>uri</em> will cause notifications to be sent. If true, than any URI values
      * at or below the specified URI will also trigger a match.
      * @param observer The object that receives callbacks when changes occur.
      * @see #unregisterContentObserver
      */
        public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
          ContentObserver observer)
        {
         registerContentObserver(uri, notifyForDescendents, observer, UserHandle.myUserId());
        }

    其中,中间这个参数notifyForDescendents的值如果为true呢,那么所有以指定Uri开头的Uri发生变化,都会触发指定的ContentObserver,就比如我们demo中,我们监听的其实是

    “content://sms”,但其实真正变化的是,"conent://sms/inbox",但是因为我们设置为true了,所以我们就能够触发对应的事件,相反,如果设置为false,就不行了。

    2.4)最后,如果我们不需要去监听内容的变化了,我们就要把它注销掉,跟广播其实一样的。

  • 相关阅读:
    User Profile Service 应用程序的新增功能
    内容管理互操作性服务 (CMIS) 连接器概述 (SharePoint Server 2010)
    AD二次开发提示“出现了一个操作错误”的解决办法
    Excel2010与MOSS2010数据同步配置
    InfoPath2010表单IE浏览器2个“微型内嵌工具”的使用和介绍
    洗礼灵魂,修炼python(1)python简介
    洗礼灵魂,修炼python(2)python安装和配置
    C语言标准定义的32个关键字
    【学习笔记】.NET知识体系结构
    【读】自定义博客园皮肤:暗色流体响应式布局
  • 原文地址:https://www.cnblogs.com/yuanting/p/5092674.html
Copyright © 2011-2022 走看看