第四篇博客就来总结下项目中使用到的一个提升用户体验的功能: Android自动填写验证码
  从字面上来看,很明显的可以看出它的实现流程:监听->有改变->获取信息->改变ui

观察者模式

  观察者模式是软件设计模式中的一种,在此模式中,一个目标物件管理所有相依于它的观察者物件,并且在本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统
  观察者模式Observer完美的将观察者和被观察的对象分离开,在模块之间划定接线,提高应用程序的可维护性和重用性。同时定义了对象间一种一对多的以来关系,以便一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动刷新。
观察者:Observer将自己注册到被观察对象中,被观察对象将观察者存放在一个容器里
被观察:当被观察者对象发生了某种变化,从容器中得到所有注册过的观察者,将变化通知观察者
撤销观察:观察者告诉被观察者要撤销观察,被观察者从容器中将观察者去除
ContentObserver
  内容观察者,目的是观察捕捉特定uri引起的数据库的变化,继而做一些相应的处理,类似于数据库中的触发器。
  流程如下:
  1. 创建ContentObserver派生类,重载onChange()方法处理回调。
  2. 利用context.getContentResolover() 获得ContentResolove对象,调用registerContentObserver()方法去注册
  3. 在不需要使用时(例如验证码获取成功后),调用unregisterContentObserver()取消注册

实现

  在我们创建的SmsObserver类中,必须要实现onChange方法,这是一个检测uri是否变化的方法。
  而验证短信发来的时候,会调用两次,第一次的uri会提示raw,此时只是表示短信到了,还没有写入数据库中。而第二次变化的时候才是我们需要接收与检测的,此时才表示短信已经被写入了数据库中。
而对于相关验证码的提取,使用正则表达式的方式,获取连续的4个数字(根据自己的需要)。
 另外,在AndroidManifest文件中添加Read_SMS的权限

相关代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
package com.screform.mmd.customview;

import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.util.Log;

import com.screform.mmd.aty.Aty_Register;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Created by lizhongquan on 15-11-18.
* Class to Observer the sms and notify to setText in editText_IdentifyingCode
*/
public class SmsObserver extends ContentObserver {

private Context context;
private Handler handler;

/**
* Creates a content observer.
*
* @param handler The handler to run {@link #onChange} on, or null if none.
*/
public SmsObserver(Context context, Handler handler) {
super(handler);

this.context = context;
this.handler = handler;
}

/**
* code ChangeListener
*
* @param selfChange change or not
* @param uri the uri of event
*/
@Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);

Log.d("info", "SMS is changed");
Log.d("info", uri.toString());

// When the sms is coming, it will be change twice.
// the first time change is not the one we need.
// The sms is not writed into the database in this time
if (uri.toString().equals("content://sms/raw")) {
return;
}

// handle the second change

// get the inboxUri
Uri inboxUri = Uri.parse("content://sms/inbox");

// query the inbox
// get the sms
Cursor mCursor = context.getContentResolver().query(inboxUri, null, null, null, "date desc");

if (null != mCursor) {
if (mCursor.moveToFirst()) {
String address = mCursor.getString(mCursor.getColumnIndex("address"));
String body = mCursor.getString(mCursor.getColumnIndex("body"));

Log.d("info", "address is :" + address + ", body is " + body);

if (address.equals("106900321100")) {
// use a regular expression to resolve and get the identifying code
// get the four consecutive numbers
Pattern pattern = Pattern.compile("(\\d{4})");
Matcher matcher = pattern.matcher(body);

if (matcher.find()){
Log.d("info", "Identifying code is " + matcher.group(0));
handler.obtainMessage(Aty_Register.GETSMS, matcher.group(0))
.sendToTarget();
}
}
}
}

// close mCursor
mCursor.close();
}
}

在Aty_Register.java中:

initObserver():

1
2
3
Uri uri = Uri.parse("content://sms");
smsObserver = new SmsObserver(Aty_Register.this, handler);
getContentResolver().registerContentObserver(uri, true, smsObserver);

onStop()中: 取消注册

1
2
3
4
5
6
@Override
protected void onStop() {
super.onStop();
getContentResolver().unregisterContentObserver(smsObserver);
Log.d("info", "unregister");
}

handler中: 更新UI

1
2
3
case GETSMS:
editText_IdentifyingCode.setText(msg.obj.toString());
break;

AndroidManifest:

1
<uses-permission android:name="android.permission.READ_SMS" />