1. 程式人生 > >驗證碼自動填充項目中觀察者模式分析

驗證碼自動填充項目中觀察者模式分析

chan span esc 好處 lint ner 解析 驗證 一對一

觀察者模式

觀察者模式定義了對象之間的一對多依賴,當一個對象狀態發生改變時,其依賴者便會收到通知並做相應的更新。其原則是:為交互對象之間松耦合。以松耦合方式在一系列對象之間溝通狀態,我們可以獨立復用主題(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

驗證碼自動填充項目中觀察者模式分析