Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android實現短信驗證碼獲取自動填寫功能(詳細版)

Android實現短信驗證碼獲取自動填寫功能(詳細版)

編輯:關於Android編程

現在的應用在注冊登錄或者修改密碼中都用到了短信驗證碼,那在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();
   }

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持本站。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved