(七)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>