zoukankan      html  css  js  c++  java
  • (七)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 程序的运行结果如下所示

     

  • 相关阅读:
    Win32应用中创建多窗口
    同时控制多个UIScrollView对象
    在后台代码中设定控件的Visibility
    VC中使用GDI+
    悬垂指针(Dangling pointer)和野指针(Wild pointer)
    在Windows下通过命令行界面编译并生成 .exe
    C++流重定向到文件
    读写文本文件和二进制文件——二进制模式
    使用MahApps.Metro
    WPF之GUI编写
  • 原文地址:https://www.cnblogs.com/fuyanan/p/4767679.html
Copyright © 2011-2022 走看看