欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2024-03-05 16:31:55
现在的应用在注册登录或者修改密码中都用到了短信验证码,那在android中是如何实现获取短信验证码并自动填写的呢? 首先,需要要在manifest中注册接收和读取短信...

现在的应用在注册登录或者修改密码中都用到了短信验证码,那在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();
   }

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