Android实现短信验证码获取自动填写功能(详细版)
作者:灰太狼 发布时间:2022-07-22 07:08:10
现在的应用在注册登录或者修改密码中都用到了短信验证码,那在android中是如何实现获取短信验证码并自动填写的呢?
首先,需要要在manifest中注册接收和读取短信的权限:
<uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>
<uses-permission android:name="android.permission.READ_SMS"/>
实现一个广播SMSBroadcastReceiver来监听短信:
package com.example.receive;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.SmsMessage;
/**
* 短信监听
* @author
*
*/
public class SMSBroadcastReceiver extends BroadcastReceiver {
private static MessageListener mMessageListener;
public static final String SMS_RECEIVED_ACTION = "android.provider.Telephony.SMS_RECEIVED";
public SMSBroadcastReceiver() {
super();
}
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(SMS_RECEIVED_ACTION)) {
Object[] pdus = (Object[]) intent.getExtras().get("pdus");
for(Object pdu:pdus) {
SmsMessage smsMessage = SmsMessage.createFromPdu((byte [])pdu);
String sender = smsMessage.getDisplayOriginatingAddress();
//短信内容
String content = smsMessage.getDisplayMessageBody();
long date = smsMessage.getTimestampMillis();
Date tiemDate = new Date(date);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = simpleDateFormat.format(tiemDate);
//过滤不需要读取的短信的发送号码
if ("+8613450214963".equals(sender)) {
mMessageListener.onReceived(content);
abortBroadcast();
}
}
}
}
//回调接口
public interface MessageListener {
public void onReceived(String message);
}
public void setOnReceivedMessageListener(MessageListener messageListener) {
this.mMessageListener = messageListener;
}
}
在需要填写验证码的Activity中,生产SMSBroadcastReceiver的实例,实现onReceived的回调接口。为了节约系统资源,我们使用动态注册注销广播的方法。
package com.example.smstest;
import com.example.receive.SMSBroadcastReceiver;
import android.os.Bundle;
import android.app.Activity;
import android.content.IntentFilter;
import android.view.Menu;
import android.widget.EditText;
public class MainActivity extends Activity {
private EditText edtPassword;
private SMSBroadcastReceiver mSMSBroadcastReceiver;
private static final String ACTION = "android.provider.Telephony.SMS_RECEIVED";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
edtPassword = (EditText) findViewById(R.id.password);
}
@Override
protected void onStart() {
super.onStart();
//生成广播处理
mSMSBroadcastReceiver = new SMSBroadcastReceiver();
//实例化过滤器并设置要过滤的广播
IntentFilter intentFilter = new IntentFilter(ACTION);
intentFilter.setPriority(Integer.MAX_VALUE);
//注册广播
this.registerReceiver(mSMSBroadcastReceiver, intentFilter);
mSMSBroadcastReceiver.setOnReceivedMessageListener(new SMSBroadcastReceiver.MessageListener() {
@Override
public void onReceived(String message) {
edtPassword.setText(message);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
//注销短信监听广播
this.unregisterReceiver(mSMSBroadcastReceiver);
}
}
上面提供了一种获取短信息验证码并自动填写的实现方式,就是直接通过短信广播监听短信。但是,这种方式有它的缺陷:当你的手机安装了其他一些短信应用(例如QQ通讯录)或者手机本身限制了权限的情况下,这种方式有可能会不起作用,无法做到自动填写,而且就算把优先级设高,也不能保证不会被别的应用“抢先”。
后来查资料知道,可以通过监听短信数据库的方式实现。监听短信数据库主要是通过ContentObserver这个类来完成。ContentObserver主要是通过Uri来监测特定的Databases的表,当ContentObserver所观察的Uri发生变化时,便会触发它。思路就是监听短信数据库 * 定号码的未读短信。我们可以通过百度找到许多demo,但是我发现很多demo中存在着Bug,在接收到短信后引起崩溃。还有一种情况,当真机连接着电脑,电脑装有类似豌豆荚之类的软件的时候,手机收到短信后,豌豆荚之类的可能会把该短信的状态改成“已读”,这样也会导致崩溃。
通过调试,终于把Bug修复了,布局和短信权限就不再赘述。在MainActivity中增加一个内部类SmsContent。
/**
* 监听短信数据库
*/
class SmsContent extends ContentObserver {
private Cursor cursor = null;
public SmsContent(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
//读取收件箱中指定号码的短信
cursor = managedQuery(Uri.parse("content://sms/inbox"), new String[]{"_id", "address", "read", "body"},
" address=? and read=?", new String[]{"1065811201", "0"}, "_id desc");//按id排序,如果按date排序的话,修改手机时间后,读取的短信就不准了
MyLog.l("cursor.isBeforeFirst() " + cursor.isBeforeFirst() + " cursor.getCount() " + cursor.getCount());
if (cursor != null && cursor.getCount() > 0) {
ContentValues values = new ContentValues();
values.put("read", "1"); //修改短信为已读模式
cursor.moveToNext();
int smsbodyColumn = cursor.getColumnIndex("body");
String smsBody = cursor.getString(smsbodyColumn);
MyLog.v("smsBody = " + smsBody);
edtPassword.setText(MatchesUtil.getDynamicPassword(smsBody));
}
//在用managedQuery的时候,不能主动调用close()方法, 否则在Android 4.0+的系统上, 会发生崩溃
if(Build.VERSION.SDK_INT < 14) {
cursor.close();
}
}
}
记得在onCreate中注册短信变化监听
SmsContent content = new SmsContent(new Handler());
//注册短信变化监听
this.getContentResolver().registerContentObserver(Uri.parse("content://sms/"), true, content);
记得注销监听
this.getContentResolver().unregisterContentObserver(content);
其中,下发的验证码短信一般都是一个字符串,其中包含6位数字,我们需要把这6位数字提取出来,我们可以用正则表达式写一个静态方法。
/**
* 从字符串中截取连续6位数字
* 用于从短信中获取动态密码
* @param str 短信内容
* @return 截取得到的6位动态密码
*/
public static String getDynamicPassword(String str) {
Pattern continuousNumberPattern = Pattern.compile("[0-9\\.]+");
Matcher m = continuousNumberPattern.matcher(str);
String dynamicPassword = "";
while(m.find()){
if(m.group().length() == 6) {
System.out.print(m.group());
dynamicPassword = m.group();
}
}
return dynamicPassword;
}
至此,android获取短信验证码并自动填写的功能就实现了。
补充:对于上面短信数据库监听中有个直接关闭游标的操作(现在已经更正):cursor.close();
但是,如果这样直接关闭的话,会引起崩溃。例如,当获取了短信密码,自动填写上了之后,按home键返回桌面,然后再进入应用,会引起应用崩溃。报的错是:
android.database.StaleDataException: Attempted to access a cursor after it has been closed
后来通过查资料得知,是用managedQuery的时候, 不能主动调用close()方法, 否则在Android 4.0+的系统上, 会发生崩溃。对版本进行一个判断再执行关闭游标的操作。
//在用managedQuery的时候,不能主动调用close()方法, 否则在Android 4.0+的系统上, 会发生崩溃
if(Build.VERSION.SDK_INT < 14) {
cursor.close();
}


猜你喜欢
- 项目里面用到了语音唤醒功能,前面一直在用讯飞的语音识别,本来打算也是直接用讯飞的语音唤醒,但是讯飞的语音唤醒要收费,试用版只有35天有效期。
- 简介相机模块库,自定义相机,通过简单的调用即可实现拍照、图片裁剪、录像及录像抓拍功能;实现图片压缩,减少图片体积;自定义相机可避免使用系统相
- 本文实例为大家分享了Java实现简单猜拳游戏的具体代码,供大家参考,具体内容如下看网上的猜拳游戏那么多,但都是用switch输入数字,所以用
- 本文章向大家介绍JAVA爬取天天基金网数据,主要包括JAVA爬取天天基金网数据使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参
- @Cacheable自定义KeyGenerator1. 概述SpringBoot 使用 @Cacheable 可以方便的管理缓存数据,在不指
- XSS是一种经常出现在web应用中的计算机安全漏洞,具体信息请自行Google。本文只分享在Spring Cloud Gateway中执行通
- 在系统开发中,需要对请求和响应分别拦截下来进行解密和加密处理,在springboot中提供了RequestBodyAdviceAdapter
- Java反射机制深入理解一.概念 反射就是把Java的各种成分映射成相应的Java类。Class类的构造方法是private,由JVM创建。
- 一、方法的定义1.方法体中最后返回值可以使用return, 如果使用了return, 那么方法体的返回值类型一定要指定2.如果方法体重没有r
- 如下所示:package cn.jdk.foreach;import java.util.HashMap;import java.util.
- 对于ApplicationListener使用Spring的应该也熟悉,因为这就是我们平时学习的观察者模式的实际代表。Spring基于Jav
- 需求前台有日期字符串的数据,提交到后台。后台实体类使用Date属性接收。日期字符串有多种格式,需要用一个转换器将合法的日期字符串格式转换为D
- 今天的几个目标: 1. 自定义ActionProvider 2. Toolbar ActionBar自定义Menu 3. Toolbar A
- 软引用简介软引用是用来表示某个引用会被GC(垃圾处理器)收集的类。当有引用指向某个obj的时候,通常发生GC的时候不会把这个对象处理掉,但是
- 最近有个项目的几张表,数量级在千万以上,技术栈是SpringBoot+Mybatis-plus+MySQL。如果使用单表,在进行查询操作,非
- 一、基本概念1、进程首先打开任务管理器,查看当前运行的进程:从任务管理器里面可以看到当前所有正在运行的进程。那么究竟什么是进程呢?进程(Pr
- 目录一、前言(1)Timer(2)DelayedQueue 延迟队列(3)ScheduledThreadPoolExecutor(4)Sch
- 一、二叉排序树定义1.二叉排序树的定义二叉排序树(Binary Sort Tree)又称二叉查找(搜索)树(Binary Search Tr
- 本文给大家分享Android视频播放器屏幕左侧边随手指上下滑动亮度调节功能的原理实现,具体代码如下所示:import android.app
- 本文使用的Unicode+DLL+Debug的方式,因为不想最后生成的exe文件太大。环境搭建步骤如下:1、下载wxWidgets包:登录w