热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android实现短信验证码获取自动填写功能(详细版)

这篇文章主要为大家详细介绍了Android实现短信验证码获取自动填写功能,很实用的功能分享给大家,感兴趣的小伙伴们可以参考一下

现在的应用在注册登录或者修改密码中都用到了短信验证码,那在android中是如何实现获取短信验证码并自动填写的呢?

首先,需要要在manifest中注册接收和读取短信的权限: 


 

实现一个广播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();
   }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 本文介绍了 AngularJS 中的 $compile 服务及其用法,通过示例代码展示了如何使用 $compile 动态编译和链接 HTML 元素。 ... [详细]
  • 用阿里云的免费 SSL 证书让网站从 HTTP 换成 HTTPS
    HTTP协议是不加密传输数据的,也就是用户跟你的网站之间传递数据有可能在途中被截获,破解传递的真实内容,所以使用不加密的HTTP的网站是不 ... [详细]
  • Python 数据可视化实战指南
    本文详细介绍如何使用 Python 进行数据可视化,涵盖从环境搭建到具体实例的全过程。 ... [详细]
  • 拼多多的崛起之路
    随着4G通信技术的发展,互联网产品从PC端转向移动端,图像传输速度更快、更清晰,智能设备的应用提升了用户体验。移动互联网的普及为拼多多的崛起提供了时代背景。 ... [详细]
  • Halcon之图像梯度、图像边缘、USM锐化
    图像梯度、图像边缘、USM锐化图像梯度、图像边缘、USM锐化图像梯度、图像边缘、USM锐化图像卷积:1.模糊2.梯度3.边缘4.锐化1.视频教程:B站、 ... [详细]
  • 360搜索引擎上线,挑战谷歌市场地位
    360公司近日推出了自家的搜索引擎,将默认搜索从谷歌替换为360搜索。这一举动引发了国内搜索引擎市场的激烈竞争,可能对谷歌在中国的市场份额产生重大影响。 ... [详细]
  • 对于众多创业公司而言,选择小程序或小视频的发展方向至关重要。本文将深入分析小程序和小视频的特点、优势及局限,帮助创业者做出更明智的选择。 ... [详细]
  • 从0到1搭建大数据平台
    从0到1搭建大数据平台 ... [详细]
  • 在 Ubuntu 中遇到 Samba 服务器故障时,尝试卸载并重新安装 Samba 发现配置文件未重新生成。本文介绍了解决该问题的方法。 ... [详细]
  • R语言及RStudio的安装步骤详解
    本文详细介绍了如何正确安装R语言及其集成开发环境RStudio。首先,需要确保安装R语言的基础环境,然后再安装RStudio。虽然先安装RStudio在技术上可行,但为了遵循最佳实践,建议先安装R语言。以下是R语言安装包的下载链接。 ... [详细]
  • 开机自启动的几种方式
    0x01快速自启动目录快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫启动或者Startup。位于该目录下的PE文件会在开机后进行自启动 ... [详细]
  • 本文提供新旧BOM(物料清单)对比的操作步骤,包括下载地址和详细操作说明。通过本指南,您可以轻松完成BOM的对比工作。 ... [详细]
  • 在2019中国国际智能产业博览会上,百度董事长兼CEO李彦宏强调,人工智能应务实推进其在各行业的应用。随后,在“ABC SUMMIT 2019百度云智峰会”上,百度展示了通过“云+AI”推动AI工业化和产业智能化的最新成果。 ... [详细]
  • MySQL下载与安装指南:详细记录常见问题及解决方法
    MySQL下载与安装指南:详细记录常见问题及解决方法 ... [详细]
  • 在CentOS 7环境中安装配置Redis及使用Redis Desktop Manager连接时的注意事项与技巧
    在 CentOS 7 环境中安装和配置 Redis 时,需要注意一些关键步骤和最佳实践。本文详细介绍了从安装 Redis 到配置其基本参数的全过程,并提供了使用 Redis Desktop Manager 连接 Redis 服务器的技巧和注意事项。此外,还探讨了如何优化性能和确保数据安全,帮助用户在生产环境中高效地管理和使用 Redis。 ... [详细]
author-avatar
恋若寒1999
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有