前言
此技术的实现原理是“广播接收者”和“内容观察者”。
利用接收到短信时的系统发出的广播进行短信的读取,并利用正则表达式解析出短信中的验证码。
在部分手机上,该广播被禁用,所以需要使用到内容观察者,在短信收件箱发生变化时,对短信数据库进行读取,得到相应验证码短信,并利用正则表达式解析出短信中的验证码。本文内容请使用于正规用途,请勿用于非法监听用户短信等恶意行为。谢谢合作!
本文原创作者xiong_it,本文原创链接:blog.csdn.net/Xiong_IT/ar…
本篇文章已授权微信公众号 guolin_blog(郭霖)独家发布.
使用广播接收者获取验证码
-
广播接收者一般用法
- 继承BroadcastReceiver
- 重写onReceive(Context context, Intent intent);
- 注册自定义广播接收者(1.代码动态注册;2.清单文件注册)
使用广播接收者获取验证码实现代码如下:
public class ReadSmsCodeReceiver extends BroadcastReceiver {
private static final String SMS_RECEIVED_ACTION =Telephony.Sms.Intents.SMS_RECEIVED_ACTION;// 接收到短信时的action
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(SMS_RECEIVED_ACTION)) {
getSmsCodeFromReceiver(intent);
}
}
/**
* 从接收者中得到短信验证码
*
* @param intent
*/
private void getSmsCodeFromReceiver(Intent intent) {
KJLoger.log(TAG, "getSmsCodeFromReceiver");
SmsMessage[] messages = null;
if (Build.VERSION.SDK_INT >= 19) {
messages = android.provider.Telephony.Sms.Intents.getMessagesFromIntent(intent);
if (messages == null) return;
} else {
messages = getSmsUnder19(intent);
if (messages == null) return;
}
if (messages.length > 0) {
SmsMessage sms = messages[0];
// 短信发送人
String smsSender = sms.getOriginatingAddress();
// 短信内容
String smsBody = sms.getMessageBody();
// 检查短信发送者,和短信内容
if (checkSmsSender(smsSender) && checkSmsBody(smsBody)) {
// 解析短信内容,得到验证码
String smsCode = parseSmsBody(smsBody);
// 自定义的方法,用来发送验证码给Handler,进行填写
sendMsg2Register(OBSERVER_SMS_CODE_MSG, smsCode);
}
}
}
/**
* 当API低于19时的做法
* @param intent
* @return 短信实例
*/
private SmsMessage[] getSmsUnder19(Intent intent) {
SmsMessage[] messages;
Bundle bundle = intent.getExtras();
// 相关链接:https://developer.android.com/reference/android/provider/Telephony.Sms.Intents.html#SMS_DELIVER_ACTION
Object[] pdus = (Object[]) bundle.get("pdus");
if ((pdus == null) || (pdus.length == 0)) {
return null;
}
messages = new SmsMessage[pdus.length];
for (int i = 0; i < pdus.length; i++) {
if (Build.VERSION.SDK_INT >= 23) {
String format = bundle.getString("format");
messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i], format);
} else {
messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
}
}
return messages;
}
}
以上,从短信广播中得到了Message实例,并得到短信。验证码获取方法为parseSmsBody(String smsBody),最后会统一讲解。
使用内容观察者得到短信验证码
-
内容观察者的一般用法
- 继承内容观察者ContentObserver
- 重写onChange(boolean selfChange)方法
- 注册内容观察者
context.getContentResolver().registerContentObserver(uri, true, mReadSmsObserver);
使用内容观察者获取验证码实现代码如下:
public class ReadSmsObserver extends ContentObserver {
private static final String SMS_INBOX_URI = "content://sms/inbox";//API level>=23,可直接使用Telephony.Sms.Inbox.CONTENT_URI,用于获取cusor
private static final String SMS_URI = "content://sms";//API level>=23,可直接使用Telephony.Sms.CONTENT_URI,用于注册内容观察者
private static final String[] PROJECTION = new String[]{
Telephony.Sms._ID,
Telephony.Sms.ADDRESS,
Telephony.Sms.BODY,
Telephony.Sms.DATE
};
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
// 读取短信收件箱,只读取未读短信,即read=0,并按照默认排序
Cursor cursor = getContentResolver().query(Uri.parse(SMS_INBOX_URI), PROJECTION,
Telephony.Sms.READ + "=?", new String[]{"0"}, Telephony.Sms.Inbox.DEFAULT_SORT_ORDER);
if (cursor == null) return;
while (cursor.moveToNext()) {
// 读取短信发送人
String address = cursor.getString(cursor.getColumnIndex(Telephony.Sms.ADDRESS));
// 读取短息内容
String smsBody = cursor.getString(cursor.getColumnIndex(Telephony.Sms.BODY));
// 检查短信发送者,和短信内容
if (checkSmsSender(address) && checkSmsBody(smsBody)) {
// 解析短信内容,得到验证码
String smsCode = parseSmsBody(smsBody);
// 自定义的方法,用来发送验证码给Handler,进行填写
sendMsg2Register(RECEIVER_SMS_CODE_MSG, smsCode);
// 关闭cursor的方法
closeCursor(cursor);
return;
}
}
// 关闭cursor的方法
closeCursor(cursor);
}
}
注意:注册短信观察者的Uri和获取Cursor的Uri不相同,如果使用获取Cursor的Uri进行注册,则得不到onChange回调。
正则表达式解析短信得到验证码
/**
* 解析短信得到验证码
*
* @param smsBody
* @return 验证码
*/
private String parseSmsBody(String smsBody) {
String regex = new String("(\\d{4})");// 匹配规则为短信中的连续4位数字
String smsCode = "";// 验证码
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(smsBody);
while (matcher.find()) {// 如果匹配到了
smsCode = matcher.group(0);
}
Log.w(TAG, "smsCode = " + smsCode);
return smsCode;
}
配置权限
<!--用于读取短信验证码,缺一不可-->
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
多说一句:笔者当时只配置了Android.permission.READ_SMS权限,结果定义的短信广播接收者死活不回调onReceive(Context context, Intent intent);
本文原创作者xiong_it,本文原创链接:blog.csdn.net/Xiong_IT/ar…
填写验证码
将上述从广播接收者,内容观察者中得到的smsCode填入EditText输入框中即可。
笔者开源了一个AutoInputAuthCode类库,用于帮助开发者快速实现该功能,博客地址:blog.csdn.net/xiong_it/ar…
Github:github.com/xiong-it/Au…
参考链接
Java正则表达式教材:www.runoob.com/java/java-r…
Android短信说明:developer.android.com/reference/a…