验证码自动填充项目中观察者模式分析

时间:2022-11-03 20:07:50

观察者模式

        观察者模式定义了对象之间的一对多依赖,当一个对象状态发生改变时,其依赖者便会收到通知并做相应的更新。其原则是:为交互对象之间松耦合。以松耦合方式在一系列对象之间沟通状态,我们可以独立复用主题(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