前提
手机获取验证码,这是每个APP都会实现的功能,但是获取验证码之后,切换到短信界面,然后再返回到当前界面,感觉比较蛋疼,也比较麻烦,因此想着获取到验证码之后自动填充这个功能相对来说就比较人性化,并且用户体验也比较好。
首先
来分析一下获取验证码并且填充的流程:手机点击获取验证码,然后平台发送短信,监测短信数据库是否有更新,并且判断是否是自己所需要的验证码短信,如果是则直接获取验证码,并且填充到当前界面中去,如下图所示:
其次
我们监测短信验证信息其中要涉及到设计模式中的观察者模式,我们这里直接继承系统的ContentObserver这个类,实现短信信息的验证,并且自动填充。现在来看看我们的实现方法吧,先上图,no pic say a xx。
-
继承ContentObserver
public class PhoneCode extends ContentObserver { private Context mContext; // 上下文 private String code; // 验证码 SmsListener mListener; private String mPhone; Cursor mCursor; public PhoneCode(Context context, Handler handler, String phone, SmsListener listener) { super(handler); this.mContext = context; this.mPhone = phone; this.mListener = listener; } public PhoneCode(Context context, Handler handler, SmsListener listener) { super(handler); this.mContext = context; this.mListener = listener; } @Override public void onChange(boolean selfChange, Uri uri) { super.onChange(selfChange); // 第一次回调 不是我们想要的 直接返回 if (uri.toString().equals("content://sms/raw")) { return; } // 第二次回调 查询收件箱里的内容 Uri inboxUri = Uri.parse("content://sms/inbox"); // 按时间顺序排序短信数据库 mCursor = mContext.getContentResolver().query(inboxUri, null, null, null, "date desc"); if (mCursor != null) { if (mCursor.moveToFirst()) { // 获取手机号 String address = mCursor.getString(mCursor.getColumnIndex("address")); Log.e("ADDRESS", address); // 获取短信内容 String body = mCursor.getString(mCursor.getColumnIndex("body")); Log.e("ADDRESS", body); // 判断手机号是否为目标号码,服务号号码不固定请用正则表达式判断前几位。 //加上这个判断必须知道发送方的电话号码,局限性比较高 // if (!address.equals(mPhone)) { // return; // } if (!body.startsWith("天下为公")) { return; } // 正则表达式截取短信中的6位验证码 String regEx = "(?<![0-9])([0-9]{" + 6 + "})(?![0-9])"; Pattern pattern = Pattern.compile(regEx); Matcher matcher = pattern.matcher(body); // 如果找到通过Handler发送给主线程 while (matcher.find()) { code = matcher.group(); if (mListener != null) { mListener.onResult(code); } } } } mCursor.close(); } /** * 短信回调接口 */ public interface SmsListener { /** * @param result 回调内容(验证码) */ void onResult(String result); } } 复制代码
-
分析
代码很简单,其中有两个构造函数,一个是带电话号码的,一个是不带电话号码的。带电话号码(电话号 码是发送方的电话号码,也就是说是平台方或者发信人的电话号码)的,这样局限性比较大,只能收到固 定的发信人的电话验证码。这里注释了,因为我想直接获取验证码,不管是谁发的短信。另外一个是不带 电话号码的验证码,这里对短信的内容加了判断。试想,如果当我们获取短信的时候收到了一条其他平台 的验证码,但却不是我们需要的,这时候该怎么办?当然是直接抛弃了,然后等待我们自己的验证码了。 因为短信验证码 都有一定的格式所以这里只需要对验证码的开头进行判断就行。当然,我们也可以和后台 同学沟通好验证码格式,然后动态判断即可,比如我这里是 以“天下为公”开头,你也可以写成变量然后动 态改变即可。 复制代码
-
使用
/** * 固定手机号码 */ private void fixedPhone(String phone) { mPhoneCode = new PhoneCode(this, new Handler(), phone, new PhoneCode.SmsListener() { @Override public void onResult(String result) { ToastUtils.showMessage(PhoneActivity.this, result); mEdtPhone.setText(result); } }); // 注册短信变化监听 this.getContentResolver().registerContentObserver( Uri.parse("content://sms/"), true, mPhoneCode); } /** * 没有手机号码 */ private void fixedPhone() { mPhoneCode = new PhoneCode(this, new Handler(), new PhoneCode.SmsListener() { @Override public void onResult(String result) { ToastUtils.showMessage(PhoneActivity.this, result); mEdtPhone.setText(result); } }); // 注册短信变化监听 this.getContentResolver().registerContentObserver( Uri.parse("content://sms/"), true, mPhoneCode); } 复制代码
-
分析
这里用的是两个使用方式,一种是带电话号码的,一个是不带的,要用哪个调那个,so easy。 复制代码
最后
这是一个很小的功能,但是写程序的时候要考虑的东西有很多。把我们自己当成使用者,用心写好一个程序,不为了完成功能而完成。实现代码一是要考虑功能,二是要考虑代码的优雅。不可不经测试和推敲就拿出去分享或者使用,不仅误导了他人,也让自己蒙羞。现在网上千篇一律没有经过推敲的文章很多,遇到过许多坑爹的,这里就发一下牢骚和提醒一下自己做事一定要认真。