前言:
在進行Android應用開發的過程中都會涉及到注冊登錄功能模塊的實現, 而許多的注冊或者登錄或者修改密碼功能常常是需要用戶去輸入短信驗證碼,通常,用戶收到短信驗證碼需要最小化應用去查看短信再次回到應用輸入相對于的驗證碼,這個過程處理有點麻煩,因此有必要應該能夠自動獲得下發成功的短信驗證碼,方便用戶操作,用戶體驗效果也會好一點。
原理講解
主要就是實時獲取短信信息,涉及到ContentObserver類的使用,使用ContentProvider來監聽短信數據庫的變化,在自定義的ContentObserver當中實現onChange 的方法進行監聽特定手機號的短信, 然后進行信息截取來填充到需要填充的位置。
ContentObserver即為內容監聽者,當我們發送一條短信到手機上時,手機會自動調用ContentObserver中的指定方法用來通知短信發生了變化,接著我們讀取短信中內容,將驗證碼提取出來自動填寫到輸入框中, 這樣就完成了自動填寫的功能。ContentObserver類主要監聽短信內容的變化, 這里涉及到Android常用的一種設計模式---【觀察者模式】。
ContentObserver 講解-觀察者模式
觀察者模式(有時又被成為發布(publish)-訂閱(Subscribe)模式,模型-視圖(View)模型,源-收聽者(Listeber)模式或從屬者模式)是軟件設計模式中的一種,在此種模式中,一個目標物件管理所有依賴于它的觀察者物件,并且在它本身的狀態改變時主動發出通知,這通常透過呼叫各種觀察者所提供的方法來實現, 此種模式通常被用來實現事件處理系統。
觀察者模式(Observer)完美的將觀察者和被觀察者的對象分離開。觀察者模式在模塊之間劃定了清晰的界限,提高了應用程序的可維護性和重用性。
觀察者設計模式定義了對象間的一種一對多的依賴關系,以便一個對象的狀態發生變化時,所有依賴于它的對象都得到通知并自動刷新。
ContentObserver——內容觀察者,目的是觀察(捕捉)特定Uri引起的數據庫的變化,繼而做一些相應的處理,它類似于數據庫技術中的觸發器(Trigger),當ContentObserver所觀察的Uri發生變化時,便會觸發它。
觀察者(即我們的應用):Observer將自己注冊到被觀察對象(Subject)中,被觀察對象將觀察者存放在一個容器(Container)里。
被觀察(即系統的短信應用):被觀察對象發生了某種變化,從容器中得到所有注冊過的觀察者,將變化通知觀察者。
撤銷觀察:觀察者告訴被觀察者要撤銷觀察,被觀察者從容器中將觀察者去除。
具體到我們的項目中,也就是說,當應用剛開始運行的時候,會向我們手機系統的短信應用注冊一個觀察者,當短信發生變化的時候,短信應用會通知所注冊的觀察者發生了變化,我們的觀察者收到這樣的通知時,就會根據代碼執行相應的操作,從而實現相關自動填寫驗證碼的功能。當我們完成所需要的功能時,我們要撤銷觀察,解除注冊,被觀察者從容器中將觀察者去除。觀察者被撤銷后不再收到短信的內容變化通知。
觀察特定Uri的步驟如下
創建我們特定的 ContentObserver 派生類,必須重載父類構造方法,必須重載 onChange() 方法去處理回調后的功能實現。
利用 context.getContentResolover() 獲得 ContentResolove 對象,接著調用 registerContentObserver() 方法去注冊內容觀察者。
由于 ContentObserver 的生命周期不同步于 Activity 和 Service 等,因此,在不需要時,需要手動的調用 unregisterContentObserver() 去取消注冊。
短信的Uri共有一下幾種:
content://sms/inbox 收件箱
content://sms/sent 已發送
content://sms/draft 草稿
content://sms/outbox 發件箱 (正在發送的信息)
content://sms/failed 發送失敗
content://sms/queued 待發送列表 (比如開啟飛行模式后,該短信就在待發送列表里)
具體實現代碼:
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;
// 讀取收件箱中含有某關鍵詞的短信
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);
}
}
}
}
/*
* 短信回調接口
*/
public interface SmsListener {
/**
* 接受sms狀態
*
* @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);
添加讀取短信的權限:
uses-permission android:name="android.permission.READ_SMS" />