前言:
在進(jìn)行Android應(yīng)用開發(fā)的過程中都會涉及到注冊登錄功能模塊的實現(xiàn), 而許多的注冊或者登錄或者修改密碼功能常常是需要用戶去輸入短信驗證碼,通常凡人,用戶收到短信驗證碼需要最小化應(yīng)用去查看短信再次回到應(yīng)用輸入相對于的驗證碼锋谐,這個過程處理有點麻煩详拙,因此有必要應(yīng)該能夠自動獲得下發(fā)成功的短信驗證碼,方便用戶操作蔓同,用戶體驗效果也會好一點饶辙。
原理講解
主要就是實時獲取短信信息,涉及到ContentObserver類的使用斑粱,使用ContentProvider來監(jiān)聽短信數(shù)據(jù)庫的變化弃揽,在自定義的ContentObserver當(dāng)中實現(xiàn)onChange 的方法進(jìn)行監(jiān)聽特定手機(jī)號的短信, 然后進(jìn)行信息截取來填充到需要填充的位置则北。
ContentObserver即為內(nèi)容監(jiān)聽者矿微,當(dāng)我們發(fā)送一條短信到手機(jī)上時,手機(jī)會自動調(diào)用ContentObserver中的指定方法用來通知短信發(fā)生了變化尚揣,接著我們讀取短信中內(nèi)容涌矢,將驗證碼提取出來自動填寫到輸入框中, 這樣就完成了自動填寫的功能快骗。ContentObserver類主要監(jiān)聽短信內(nèi)容的變化娜庇, 這里涉及到Android常用的一種設(shè)計模式---【觀察者模式】。
ContentObserver 講解-觀察者模式
觀察者模式(有時又被成為發(fā)布(publish)-訂閱(Subscribe)模式滨巴,模型-視圖(View)模型思灌,源-收聽者(Listeber)模式或從屬者模式)是軟件設(shè)計模式中的一種,在此種模式中恭取,一個目標(biāo)物件管理所有依賴于它的觀察者物件泰偿,并且在它本身的狀態(tài)改變時主動發(fā)出通知,這通常透過呼叫各種觀察者所提供的方法來實現(xiàn)蜈垮, 此種模式通常被用來實現(xiàn)事件處理系統(tǒng)耗跛。
觀察者模式(Observer)完美的將觀察者和被觀察者的對象分離開。觀察者模式在模塊之間劃定了清晰的界限攒发,提高了應(yīng)用程序的可維護(hù)性和重用性调塌。
觀察者設(shè)計模式定義了對象間的一種一對多的依賴關(guān)系,以便一個對象的狀態(tài)發(fā)生變化時惠猿,所有依賴于它的對象都得到通知并自動刷新羔砾。
ContentObserver——內(nèi)容觀察者,目的是觀察(捕捉)特定Uri引起的數(shù)據(jù)庫的變化偶妖,繼而做一些相應(yīng)的處理姜凄,它類似于數(shù)據(jù)庫技術(shù)中的觸發(fā)器(Trigger),當(dāng)ContentObserver所觀察的Uri發(fā)生變化時趾访,便會觸發(fā)它态秧。
觀察者(即我們的應(yīng)用):Observer將自己注冊到被觀察對象(Subject)中,被觀察對象將觀察者存放在一個容器(Container)里扼鞋。
被觀察(即系統(tǒng)的短信應(yīng)用):被觀察對象發(fā)生了某種變化申鱼,從容器中得到所有注冊過的觀察者愤诱,將變化通知觀察者。
撤銷觀察:觀察者告訴被觀察者要撤銷觀察捐友,被觀察者從容器中將觀察者去除淫半。
具體到我們的項目中,也就是說楚殿,當(dāng)應(yīng)用剛開始運(yùn)行的時候撮慨,會向我們手機(jī)系統(tǒng)的短信應(yīng)用注冊一個觀察者,當(dāng)短信發(fā)生變化的時候脆粥,短信應(yīng)用會通知所注冊的觀察者發(fā)生了變化砌溺,我們的觀察者收到這樣的通知時,就會根據(jù)代碼執(zhí)行相應(yīng)的操作变隔,從而實現(xiàn)相關(guān)自動填寫驗證碼的功能规伐。當(dāng)我們完成所需要的功能時,我們要撤銷觀察匣缘,解除注冊猖闪,被觀察者從容器中將觀察者去除。觀察者被撤銷后不再收到短信的內(nèi)容變化通知肌厨。
觀察特定Uri的步驟如下
創(chuàng)建我們特定的 ContentObserver 派生類培慌,必須重載父類構(gòu)造方法,必須重載 onChange() 方法去處理回調(diào)后的功能實現(xiàn)柑爸。
利用 context.getContentResolover() 獲得 ContentResolove 對象吵护,接著調(diào)用 registerContentObserver() 方法去注冊內(nèi)容觀察者。
由于 ContentObserver 的生命周期不同步于 Activity 和 Service 等表鳍,因此馅而,在不需要時,需要手動的調(diào)用 unregisterContentObserver() 去取消注冊譬圣。
短信的Uri共有一下幾種:
content://sms/inbox 收件箱
content://sms/sent 已發(fā)送
content://sms/draft 草稿
content://sms/outbox 發(fā)件箱 (正在發(fā)送的信息)
content://sms/failed 發(fā)送失敗
content://sms/queued 待發(fā)送列表 (比如開啟飛行模式后瓮恭,該短信就在待發(fā)送列表里)
具體實現(xiàn)代碼:
public class SmsObserver extends ContentObserver {
public static final String SMS_URI_INBOX = "content://sms/inbox";
private Activity activity = null;
private String smsContent = "";
private SmsListener listener;
public SmsObserver(Activity activity, Handler handler, SmsListener listener) {
super(handler);
this.activity = activity;
this.listener = listener;
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Cursor cursor = null;
// 讀取收件箱中含有某關(guān)鍵詞的短信
ContentResolver contentResolver = activity.getContentResolver();
cursor = contentResolver.query(Uri.parse(SMS_URI_INBOX), new String[] {
"_id", "address", "body", "read" }, "body like ? and read=?",
new String[] { "%快遞%", "0" }, "date desc");
if (cursor != null) {
cursor.moveToFirst();
if (cursor.moveToFirst()) {
String smsbody = cursor
.getString(cursor.getColumnIndex("body"));
String regEx = "[^0-9]";
Pattern p = Pattern.compile(regEx);
Matcher m = p.matcher(smsbody.toString());
smsContent = m.replaceAll("").trim().toString();
if (!TextUtils.isEmpty(smsContent)) {
listener.onResult(smsContent);
}
}
}
}
/*
* 短信回調(diào)接口
*/
public interface SmsListener {
/**
* 接受sms狀態(tài)
*
* @Title: onResult
*/
void onResult(String smsContent);
}
}
使用如下:
SmsObserver smsObserver = new SmsObserver(this, new Handler(),
new SmsListener() {
@Override
public void onResult(String smsContent) {
//todo
}
});
this.getContentResolver().registerContentObserver(
Uri.parse("content://sms/"), true, smsObserver);
添加讀取短信的權(quán)限:
uses-permission android:name="android.permission.READ_SMS" />