驗證碼自動填充項目中觀察者模式分析
觀察者模式
觀察者模式定義了對象之間的一對多依賴,當一個對象狀態發生改變時,其依賴者便會收到通知並做相應的更新。其原則是:為交互對象之間松耦合。以松耦合方式在一系列對象之間溝通狀態,我們可以獨立復用主題(Subject)/可觀測者(Observable)和觀測者(Observer),即只要遵守接口規則改變其中一方並不會影響到另一方。這也是其主要的設計原則。
舉個例子:老師留高軟作業,所有學生收到作業信息。老師對應多個學生,留作業是發生的變化,學生會收到作業信息。老師(或學生)通常步行去學校,今天決定以後都坐車去學校的變化,並不會影響學生(或老師)的去學校方式。
以面向對象語言為例:主題類負責實現觀察者的註冊、註銷、通知觀察者;觀察者類負責更新信息。
SMSContentObserver
https://github.com/88ios/SMSContentObserver-master/tree/master/app/src/main/java/com/aikaifa/message
以短信驗證碼自動填充項目為例:
為實現短信驗證碼自動填充,就要求對信箱進行監聽,當信箱收到特定地址發來的信件,觀察者執行讀取信息並解析,符合要求後,將執行信息填充的操作。
在該例子中,信箱即為主題,負責完成觀察者的註冊、註銷和通知:
主題類(public class MainActivity extends AppCompatActivity),在類中持有一個觀察者類MessageContentObserver的私有對象。
https://github.com/88ios/SMSContentObserver-master/blob/master/app/src/main/java/com/aikaifa/message/MainActivity.java
1.註冊
1 protected void onCreate(Bundle savedInstanceState) { 2 super.onCreate(savedInstanceState); 3 setContentView(R.layout.activity_main); 4 codeEdt = (EditText) findViewById(R.id.smsCode);5 findViewById(R.id.send_sms_btn).setOnClickListener(new View.OnClickListener() { 6 @Override 7 public void onClick(View v) { 8 senSMSCode(); 9 } 10 }); 11 messageContentObserver = new MessageContentObserver(MainActivity.this, handler); 12 getContentResolver().registerContentObserver(Uri.parse("content://sms/"), true, messageContentObserver); 13 }
方法完成了對觀察者的註冊。邏輯為先點擊信息發送,同時註冊觀察者。
2.註銷
1 protected void onDestroy() { 2 super.onDestroy(); 3 getContentResolver().unregisterContentObserver(messageContentObserver); 4 }
方法完成了對觀察者的註銷。由於沒有測試代碼,我推測調用該方法應該是每過周期性的一段時間進行調用。
3.通知
1 private void senSMSCode() { 2 BmobSMS.requestSMSCode("13433962858", "aikaifa", new QueryListener<Integer>() { 3 @Override 4 public void done(Integer smsId, BmobException ex) { 5 if (ex == null) {//驗證碼發送成功 6 Log.i("smile", "短信id:" + smsId);//用於後續的查詢本次短信發送狀態 7 } 8 } 9 }); 10 }
該方法完成信息發送,邏輯上只是點擊發送所執行的業務。而下一個方法完成了所收到信息的獲取。
1 @SuppressLint("HandlerLeak") 2 Handler handler = new Handler() { 3 @Overrid。 4 public void handleMessage(Message msg) { 5 if (msg.what == MSG_RECEIVE_CODE) { 6 codeEdt.setText(msg.obj.toString()); //設置讀取到的內容 7 } 8 } 9 };
到現在為止也沒有完成確切的通知,而這個通知實際上在註冊觀察者時已經隱性通知過了。
似乎有點矛盾,理解一下這個過程:我要實現信箱收到驗證碼信息,然後自動填充的過程。但是這個驗證碼必須是你先發送信息請求來的,這個發送信息實際上是最深層次的變化,信箱收到驗證碼才是表面變化。所以,你先發送信息(發生了變化),從信箱獲取驗證碼(通知觀察者的信息),註冊觀察者,並傳遞信息(通知觀察者)。
觀察者類
https://github.com/88ios/SMSContentObserver-master/blob/master/app/src/main/java/com/aikaifa/message/MessageContentObserver.java
更新
1 public void onChange(boolean selfChange, Uri uri) { 2 Log.e("tag", uri.toString()); 3 if (uri.toString().equals("content://sms/raw")) { // 第一次回調 4 return; 5 } 6 Uri inboxUri = Uri.parse("content://sms/inbox"); // 第二次回調 查詢收件箱裏的內容 7 Cursor c = mContext.getContentResolver().query(inboxUri, null, null, null, "date desc"); // 按時間順序排序短信數據庫 8 if (c != null) { 9 if (c.moveToFirst()) { 10 String address = c.getString(c.getColumnIndex("address"));//發送方號碼 11 String body = c.getString(c.getColumnIndex("body")); // 短信內容 12 if (!address.equals("10086")) { 13 return; 14 } 15 Pattern pattern = Pattern.compile("(\\d{6})");//正則表達式匹配驗證碼 16 Matcher matcher = pattern.matcher(body); 17 if (matcher.find()) { 18 code = matcher.group(0); 19 Message msg = Message.obtain(); 20 msg.what = MainActivity.MSG_RECEIVE_CODE; 21 msg.obj = code; 22 mHandler.sendMessage(msg); 23 } 24 } 25 c.close(); 26 } 27 }
這個方法會兩次調用,因為涉及查詢信箱信件內容,而收到短信不會立即寫入信箱,所以需要兩次調用。這個方法完成發送地址信息匹配和自動寫入操作。
總結
在這個項目中利用觀察者設計模式完成了驗證碼自動寫入的功能。仔細思考一下過程:發送信息、等待驗證碼、註冊觀察者、通知觀察者和觀察者處理。
由於在這個項目中是對特定驗證碼進行自動寫入,邏輯上是以觀察者模式進行的,而實際上實現了對收到信息進行驗證,如果匹配則寫入的一對一依賴關系。不用觀察者模式也可以完成(一對一不用註冊,直接處理通知信息)。
可以對其擴展,加入對不同發送地址的驗證碼寫入功能(轉化為一對多)。使其轉變為將信箱為主題,新收到的驗證碼為變化,不同的發送地址為不同的觀察者。這樣,就必須使用觀察者模式,好處是不用反復編寫不同地址發來驗證碼的通知方法,代碼復用性好,其次模塊之間耦合度低,利於模塊內功能拓展,比如發送短信功能的開發。
地址項目:https://github.com/88ios/SMSContentObserver-master/tree/master/app/src/main/java/com/aikaifa/message
驗證碼自動填充項目中觀察者模式分析