国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 系統(tǒng) > Android > 正文

Android實(shí)現(xiàn)短信驗(yàn)證碼獲取自動(dòng)填寫(xiě)功能(詳細(xì)版)

2019-12-12 05:49:09
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

現(xiàn)在的應(yīng)用在注冊(cè)登錄或者修改密碼中都用到了短信驗(yàn)證碼,那在android中是如何實(shí)現(xiàn)獲取短信驗(yàn)證碼并自動(dòng)填寫(xiě)的呢?

首先,需要要在manifest中注冊(cè)接收和讀取短信的權(quán)限: 

<uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>
<uses-permission android:name="android.permission.READ_SMS"/> 

實(shí)現(xiàn)一個(gè)廣播SMSBroadcastReceiver來(lái)監(jiān)聽(tīng)短信:

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;/** * 短信監(jiān)聽(tīng) * @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();     //短信內(nèi)容     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);     //過(guò)濾不需要讀取的短信的發(fā)送號(hào)碼     if ("+8613450214963".equals(sender)) {      mMessageListener.onReceived(content);      abortBroadcast();     }    }   }   }  //回調(diào)接口 public interface MessageListener {  public void onReceived(String message); }  public void setOnReceivedMessageListener(MessageListener messageListener) {  this.mMessageListener = messageListener; }}

在需要填寫(xiě)驗(yàn)證碼的Activity中,生產(chǎn)SMSBroadcastReceiver的實(shí)例,實(shí)現(xiàn)onReceived的回調(diào)接口。為了節(jié)約系統(tǒng)資源,我們使用動(dòng)態(tài)注冊(cè)注銷(xiāo)廣播的方法。 

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();  //實(shí)例化過(guò)濾器并設(shè)置要過(guò)濾的廣播  IntentFilter intentFilter = new IntentFilter(ACTION);  intentFilter.setPriority(Integer.MAX_VALUE);  //注冊(cè)廣播  this.registerReceiver(mSMSBroadcastReceiver, intentFilter);  mSMSBroadcastReceiver.setOnReceivedMessageListener(new SMSBroadcastReceiver.MessageListener() {   @Override   public void onReceived(String message) {    edtPassword.setText(message);   }  }); }  @Override protected void onDestroy() { super.onDestroy(); //注銷(xiāo)短信監(jiān)聽(tīng)廣播  this.unregisterReceiver(mSMSBroadcastReceiver); }}

上面提供了一種獲取短信息驗(yàn)證碼并自動(dòng)填寫(xiě)的實(shí)現(xiàn)方式,就是直接通過(guò)短信廣播監(jiān)聽(tīng)短信。但是,這種方式有它的缺陷:當(dāng)你的手機(jī)安裝了其他一些短信應(yīng)用(例如QQ通訊錄)或者手機(jī)本身限制了權(quán)限的情況下,這種方式有可能會(huì)不起作用,無(wú)法做到自動(dòng)填寫(xiě),而且就算把優(yōu)先級(jí)設(shè)高,也不能保證不會(huì)被別的應(yīng)用“搶先”。

后來(lái)查資料知道,可以通過(guò)監(jiān)聽(tīng)短信數(shù)據(jù)庫(kù)的方式實(shí)現(xiàn)。監(jiān)聽(tīng)短信數(shù)據(jù)庫(kù)主要是通過(guò)ContentObserver這個(gè)類(lèi)來(lái)完成。ContentObserver主要是通過(guò)Uri來(lái)監(jiān)測(cè)特定的Databases的表,當(dāng)ContentObserver所觀(guān)察的Uri發(fā)生變化時(shí),便會(huì)觸發(fā)它。思路就是監(jiān)聽(tīng)短信數(shù)據(jù)庫(kù)中特定號(hào)碼的未讀短信。我們可以通過(guò)百度找到許多demo,但是我發(fā)現(xiàn)很多demo中存在著B(niǎo)ug,在接收到短信后引起崩潰。還有一種情況,當(dāng)真機(jī)連接著電腦,電腦裝有類(lèi)似豌豆莢之類(lèi)的軟件的時(shí)候,手機(jī)收到短信后,豌豆莢之類(lèi)的可能會(huì)把該短信的狀態(tài)改成“已讀”,這樣也會(huì)導(dǎo)致崩潰。

通過(guò)調(diào)試,終于把Bug修復(fù)了,布局和短信權(quán)限就不再贅述。在MainActivity中增加一個(gè)內(nèi)部類(lèi)SmsContent。

  /**  * 監(jiān)聽(tīng)短信數(shù)據(jù)庫(kù)  */ class SmsContent extends ContentObserver {  private Cursor cursor = null;  public SmsContent(Handler handler) {   super(handler);  }  @Override  public void onChange(boolean selfChange) {   super.onChange(selfChange);   //讀取收件箱中指定號(hào)碼的短信   cursor = managedQuery(Uri.parse("content://sms/inbox"), new String[]{"_id", "address", "read", "body"},     " address=? and read=?", new String[]{"1065811201", "0"}, "_id desc");//按id排序,如果按date排序的話(huà),修改手機(jī)時(shí)間后,讀取的短信就不準(zhǔn)了   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的時(shí)候,不能主動(dòng)調(diào)用close()方法, 否則在Android 4.0+的系統(tǒng)上, 會(huì)發(fā)生崩潰   if(Build.VERSION.SDK_INT < 14) {    cursor.close();   }  } } 

記得在onCreate中注冊(cè)短信變化監(jiān)聽(tīng) 

SmsContent content = new SmsContent(new Handler());  //注冊(cè)短信變化監(jiān)聽(tīng)  this.getContentResolver().registerContentObserver(Uri.parse("content://sms/"), true, content); 

記得注銷(xiāo)監(jiān)聽(tīng)
 this.getContentResolver().unregisterContentObserver(content);

其中,下發(fā)的驗(yàn)證碼短信一般都是一個(gè)字符串,其中包含6位數(shù)字,我們需要把這6位數(shù)字提取出來(lái),我們可以用正則表達(dá)式寫(xiě)一個(gè)靜態(tài)方法。 

 /**  * 從字符串中截取連續(xù)6位數(shù)字  * 用于從短信中獲取動(dòng)態(tài)密碼  * @param str 短信內(nèi)容  * @return 截取得到的6位動(dòng)態(tài)密碼  */ 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獲取短信驗(yàn)證碼并自動(dòng)填寫(xiě)的功能就實(shí)現(xiàn)了。

補(bǔ)充:對(duì)于上面短信數(shù)據(jù)庫(kù)監(jiān)聽(tīng)中有個(gè)直接關(guān)閉游標(biāo)的操作(現(xiàn)在已經(jīng)更正):cursor.close();  
但是,如果這樣直接關(guān)閉的話(huà),會(huì)引起崩潰。例如,當(dāng)獲取了短信密碼,自動(dòng)填寫(xiě)上了之后,按home鍵返回桌面,然后再進(jìn)入應(yīng)用,會(huì)引起應(yīng)用崩潰。報(bào)的錯(cuò)是:

android.database.StaleDataException: Attempted to access a cursor after it has been closed

后來(lái)通過(guò)查資料得知,是用managedQuery的時(shí)候, 不能主動(dòng)調(diào)用close()方法, 否則在Android 4.0+的系統(tǒng)上, 會(huì)發(fā)生崩潰。對(duì)版本進(jìn)行一個(gè)判斷再執(zhí)行關(guān)閉游標(biāo)的操作。 

//在用managedQuery的時(shí)候,不能主動(dòng)調(diào)用close()方法, 否則在Android 4.0+的系統(tǒng)上, 會(huì)發(fā)生崩潰   if(Build.VERSION.SDK_INT < 14) {    cursor.close();   }

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持武林網(wǎng)。

發(fā)表評(píng)論 共有條評(píng)論
用戶(hù)名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 阳江市| 鹰潭市| 区。| 纳雍县| 南郑县| 汉中市| 镇平县| 固安县| 博罗县| 西城区| 西贡区| 云霄县| 洛扎县| 临颍县| 临夏市| 长春市| 新干县| 洞头县| 高碑店市| 阿勒泰市| 大冶市| 稷山县| 绵阳市| 马龙县| 麻城市| 静海县| 乐山市| 顺平县| 星子县| 莎车县| 越西县| 凤山市| 庄浪县| 朝阳县| 偃师市| 丰镇市| 金平| 清新县| 华安县| 湟源县| 德化县|