(七)android开发中两种方式监听短信的原理和实现

一、监听短信的两种方式的简介    

Android程序开发中,有两种方式监听短信内容:一、接收系统的短信广播;二、应用观察者模式,监听短信数据库。

第一种方式接收系统的短信广播: A、这种方式只对新收到的短消息有效,运行代码,并不会读取收件箱中已读或未读的消息,只有当收到新来的短消息时,才会执行onReceive()方法。

                               B、并且这个广播是有序广播,如果当别的程序先读取到了这个广播,然后拦截掉了个这个广播,你将接收不到。当然我们可以通过设置priority的数值,其实有时是不管用的,现在在一些定制的系统或是有安全软件的情况下,往往短消息都被截取到,并被干掉。

第二种方式应用观察者模式,监听短信数据库:这种方式比方法一稍微复杂一些,不过使用起来也很方便,不受其它程序干扰,并且这种方式可以获取手机上的收件箱、已发送、草稿箱等等数据库的变化。

二、接收系统的短信广播方式的实现

2.1 在AndroidManifest.xml中添加权限

<uses-permission android:name="android.permission.RECEIVE_SMS" /> <!-- 接收短信权限 -->
<uses-permission android:name="android.permission.READ_SMS" /> <!-- 读取短信权限 -->

2.2 在AndroidManifest.xml中注册 广播

<receiver android:name="com.example.smslistenerdemo.SmsReceiver" >
            <intent-filter android:priority="2147483647" >
                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            </intent-filter>
 </receiver>

2.3 SmsReceiver.java中的代码如下所示

package com.example.smslistenerdemo;

import java.text.SimpleDateFormat;
import java.util.Date;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.util.Log;

/**
 * 类说明:
 * 
 * @author fuyanan
 * @date 2015-8-28
 * @version 1.0.0
 */
public class SmsReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Bundle bundle = intent.getExtras();
        SmsMessage msg = null;
        if (null != bundle) {
            Object[] smsObj = (Object[]) bundle.get("pdus");
            for (Object object : smsObj) {
                msg = SmsMessage.createFromPdu((byte[]) object);
                Date date = new Date(msg.getTimestampMillis());// 时间
                SimpleDateFormat format = new SimpleDateFormat(
                        "yyyy-MM-dd HH:mm:ss");
                String receiveTime = format.format(date);
                Log.i("fuyanan", "address:" + msg.getOriginatingAddress()
                        + "   body:" + msg.getDisplayMessageBody() + "  time:"
                        + msg.getTimestampMillis());
            }
        }
    }
}

三、应用观察者模式,监听短信数据库

3.1 ContentObserver简介 

ContentObserver——内容观察者,目的是观察(捕捉)指定Uri引起的数据库的变化,继而做一些相应的处理,它类似于数据库技术中的触发器(Trigger),当ContentObserver所观察的Uri发生变哈时,便会触发它。

抽象类ContentResolver类中的方法原型如下,注册/取消注册ContentObserver方法

(1)注册: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的派生类实例

(2)取消注册:public final void  unregisterContentObserver(ContentObserver observer)

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

3.2  应用观察者模式,监听短信数据库的实现demo

3.2.1  在AndroidManifest.xml中添加权限

  <uses-permission android:name="android.permission.RECEIVE_SMS" /> <!-- 接收短信权限 -->
  <uses-permission android:name="android.permission.READ_SMS" /> <!-- 读取短信权限 -->

3.2.2 SMSContentObserver.java中的代码如下所示

package com.example.smslistenerdemo;

import android.content.Context;
import android.database.ContentObserver;
import android.os.Handler;

/**
 * 类说明:监听短信有两种方式:第一通过接受系统短息广播;第二监听短信数据库 
 * 本类是用来观察系统里短信收件箱的数据库的变化,只要短信收件箱数据库发生变化,就会触发该类。
 * 
 * @author fuyn
 * @date 2015-7-20
 * @version 1.0.0
 */
public class SMSContentObserver extends ContentObserver {
    private static final int MSG_INBOX = 1;
    private Context mContext;
    private Handler mHandler; // 更新UI线程

    public SMSContentObserver(Context mContext,
            Handler mHandler) {
        super(mHandler); // 所有ContentObserver的派生类都需要调用该构造方法
        this.mContext = mContext;
        this.mHandler = mHandler;
    }

    /**
     * 当观察到的Uri发生变化时,回调该方法去处理。所有ContentObserver的派生类都需要重载该方法去处理逻辑
     * selfChange:回调后,其值一般为false,该参数意义不大
     */
    @Override
    public void onChange(boolean selfChange) {
        // TODO Auto-generated method stub
        super.onChange(selfChange);
        mHandler.obtainMessage(MSG_INBOX, "SMS Received").sendToTarget(); 
    }

}

3.2.3 MainActivity.java中的代码如下所示

package com.example.smslistenerdemo;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

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.util.Log;
import android.widget.EditText;

public class MainActivity extends Activity {
    private EditText login_et_sms_code;
    private SMSContentObserver smsContentObserver;
    protected static final int MSG_INBOX = 1;
    private Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MSG_INBOX:
                setSmsCode();
                break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        login_et_sms_code = (EditText) this
                .findViewById(R.id.login_et_sms_code);
        smsContentObserver = new SMSContentObserver(MainActivity.this, mHandler);
    }

    private void setSmsCode() {
        Cursor cursor = null;
        // 添加异常捕捉
        try {
            cursor = getContentResolver().query(
                    Uri.parse("content://sms/inbox"),
                    new String[] { "_id", "address", "read", "body", "date" },
                    null, null, "date desc"); // datephone想要的短信号码
            if (cursor != null) { // 当接受到的新短信与想要的短信做相应判断
                String body = "";
                while (cursor.moveToNext()) {
                    body = cursor.getString(cursor.getColumnIndex("body"));// 在这里获取短信信息
                    long smsdate = Long.parseLong(cursor.getString(cursor
                            .getColumnIndex("date")));
                    long nowdate = System.currentTimeMillis();
                    // 如果当前时间和短信时间间隔超过60秒,认为这条短信无效
                    if (nowdate - smsdate > 60 * 1000) {
                        break;
                    }
                    // 下面匹配验证码
                    Pattern pattern = Pattern.compile("\\d{6}");
                    Matcher matcher = pattern.matcher(body);
                    if (matcher.find()) {
                        String smsCodeStr = matcher.group(0);
                        Log.i("fuyanan", "sms find: code=" + matcher.group(0));// 打印出匹配到的验证码
                        login_et_sms_code.setText(smsCodeStr);
                        break;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    @Override
    protected void onResume() {
        // TODO Auto-generated method stub
        super.onResume();
        if (smsContentObserver != null) {
            getContentResolver().registerContentObserver(
                    Uri.parse("content://sms/"), true, smsContentObserver);// 注册监听短信数据库的变化
        }
    }

    @Override
    protected void onPause() {
        // TODO Auto-generated method stub
        super.onPause();
        if (smsContentObserver != null) {
            getContentResolver().unregisterContentObserver(smsContentObserver);// 取消监听短信数据库的变化
        }

    }

}

3.2.4 activity_main.xml中的代码如下所示

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <EditText
        android:id="@+id/login_et_sms_code"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="15dp"
        android:cursorVisible="false"
        android:hint="请输入验证码"
        android:inputType="number" />

</RelativeLayout>

3.2.5 程序的运行结果如下所示

 

posted @ 2015-08-28 19:30  小菜美妞成长中  阅读(8384)  评论(0编辑  收藏  举报