Android短信验证码自动填充
egret0502
8年前
<p>在我们的Android开发中会经常用到短信验证的功能,这个时候如果再让用户就查看短信.然后再回到界面进行短信的填写,难免有多少有些不方便,为了提高用户体验,我也就自己来实现验证码的自动填写功能</p> <h2>一、简介</h2> <p>之前笔者见过一些人的实现思路是这样的:</p> <p>创建一个广播接收器,接受短信变化的广播,然后在收到广播的时候,再把验证码提取出来发送给我们的需要填充验证码的地方就行了。 但是,这种方式有它的缺陷:当你的手机安装了其他一些短信应用(例如QQ通讯录)或者手机本身限制了权限的情况下,这种方式有可能会不起作用,无法做到自动填写,而且就算把优先级设高,也不能保证不会被别的应用“抢先”。 所以,通过查阅资料,发现了这种方式: 可以通过监听短信数据库的方式实现。监听短信数据库主要是通过ContentObserver这个类来完成。ContentObserver主要是通过Uri来监测特定的Databases的表,当ContentObserver所观察的Uri发生变化时,便会触发它。</p> <h2>二、短信验证码自动填充的工具类</h2> <pre> <code class="language-java">/** * 短信验证码自动填充 * * @author 周旭 */ public class SMSContentObserver extends ContentObserver { private Context mContext; // 上下文 private Handler mHandler; // 更新UI线程 private String code; // 验证码 public SMSContentObserver(Context context, Handler handler) { super(handler); mContext = context; mHandler = handler; } /** * 回调函数, 当所监听的Uri发生改变时,就会回调此方法 * <p> * 注意当收到短信的时候会回调两次 * 收到短信一般来说都是执行了两次onchange方法.第一次一般都是raw的这个. * 这个时候虽然收到了短信.但是短信还没有写入到收件箱里面 * 然后才是另外一个,后面的数字是该短信在收件箱中的位置 * * @param selfChange 此值意义不大 一般情况下该回调值false */ @Override public void onChange(boolean selfChange, Uri uri) { Log.e("tag", uri.toString()); // 第一次回调 不是我们想要的 直接返回 if (uri.toString().equals("content://sms/raw")) { return; } // 第二次回调 查询收件箱里的内容 Uri inboxUri = Uri.parse("content://sms/inbox"); // 按时间顺序排序短信数据库 Cursor c = mContext.getContentResolver().query(inboxUri, null, null, null, "date desc"); if (c != null) { if (c.moveToFirst()) { // 获取短信提供商的手机号 String address = c.getString(c.getColumnIndex("address")); // 获取短信内容 String body = c.getString(c.getColumnIndex("body")); Log.i("tag", "body------->" + body); // 判断手机号是否为目标号码(短信提供商的号码) // 在这里我们的短信提供商的号码如果是固定的话.我们可以再加一个判断,这样就不会受到别的短信应用的验证码的影响了 // 不然的话就在我们的正则表达式中,加一些自己的判断,例如短信中含有自己应用名字啊什么的... /*if (!address.equals("13342290623")) { Log.i("tag","------->没有读取到内容"); return; }*/ // 正则表达式截取短信中的6位验证码 Pattern pattern = Pattern.compile("(\\d{6})"); Matcher matcher = pattern.matcher(body); // 利用handler将得到的验证码发送给主线程 if (matcher.find()) { code = matcher.group(0); //mHandler.obtainMessage(1, code).sendToTarget(); Message msg = Message.obtain(); msg.what = MainActivity.MSG_RECEIVE_CODE; msg.obj = code; mHandler.sendMessage(msg); } } c.close(); } } }</code></pre> <p>调用的Activity:</p> <pre> <code class="language-java">/** * 短信验证码自动填写功能的实现 */ public class MainActivity extends AppCompatActivity { public static final int MSG_RECEIVE_CODE = 1; //收到短信的验证码 private EditText codeEdt; //短信验证码的输入框 private SMSContentObserver smsContentObserver; //回调接口 @SuppressLint("HandlerLeak") Handler handler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == MSG_RECEIVE_CODE) { codeEdt.setText(msg.obj.toString()); //设置读取到的内容 } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); codeEdt = (EditText) findViewById(R.id.smsCode); findViewById(R.id.send_sms_btn).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { senSMSCode(); } }); smsContentObserver = new SMSContentObserver( MainActivity.this, handler); //ContentObserver注册 getContentResolver().registerContentObserver( Uri.parse("content://sms/"), true, smsContentObserver); } /** * 取消注册 */ @Override protected void onDestroy() { super.onDestroy(); getContentResolver().unregisterContentObserver(smsContentObserver); } //使用Bmob云的短信验证功能来发送短信 private void senSMSCode() { BmobSMS.requestSMSCode("13342290623", "知么网络", new QueryListener<Integer>() { @Override public void done(Integer smsId, BmobException ex) { if (ex == null) {//验证码发送成功 Log.i("smile", "短信id:" + smsId);//用于后续的查询本次短信发送状态 Toast.makeText(MainActivity.this, "发送验证码成功", Toast.LENGTH_SHORT).show(); } } }); } }</code></pre> <p>添加权限</p> <pre> <code class="language-java"><!--读取短信的权限--> <uses-permission android:name="android.permission.RECEIVE_SMS"/> <uses-permission android:name="android.permission.READ_SMS" /></code></pre> <h3>三、短信的Uri共有一下几种:</h3> <p>content://sms/inbox 收件箱</p> <p>content://sms/sent 已发送</p> <p>content://sms/draft 草稿</p> <p>content://sms/outbox 发件箱 (正在发送的信息)</p> <p>content://sms/failed 发送失败</p> <p>content://sms/queued 待发送列表 (比如开启飞行模式后,该短信就在待发送列表里)</p> <p> </p>