美文网首页Android Devapp开发Android研究院
Android短信验证码自动填充

Android短信验证码自动填充

作者: 唠嗑008 | 来源:发表于2017-01-14 21:00 被阅读966次

    github地址

    https://github.com/zhouxu88/SMSContentObserver

    ** 在我们的Android开发中会经常用到短信验证的功能,这个时候如果再让用户就查看短信.然后再回到界面进行短信的填写,难免有多少有些不方便,为了提高用户体验,我也就自己来实现验证码的自动填写功能**

    一、简介

    之前笔者见过一些人的实现思路是这样的:
    创建一个广播接收器,接受短信变化的广播,然后在收到广播的时候,再把验证码提取出来发送给我们的需要填充验证码的地方就行了。但是,这种方式有它的缺陷:当你的手机安装了其他一些短信应用(例如QQ通讯录)或者手机本身限制了权限的情况下,这种方式有可能会不起作用,无法做到自动填写,而且就算把优先级设高,也不能保证不会被别的应用“抢先”。所以,通过查阅资料,发现了这种方式:可以通过监听短信数据库的方式实现。监听短信数据库主要是通过ContentObserver这个类来完成。ContentObserver主要是通过Uri来监测特定的Databases的表,当ContentObserver所观察的Uri发生变化时,便会触发它。ContentObserver——内容观察者,可监听观察特定Uri指向的数据库项的变化,进而进行相应的处理。

    二、短信验证码自动填充的工具类

    /**
     * 短信验证码自动填充
     *
     * @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();
            }
        }
    }
    
    

    调用的Activity:

    /**
     * 短信验证码自动填写功能的实现
     */
    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();
                    }
                }
            });
    
        }
    }
    

    添加权限

    <!--读取短信的权限-->
    <uses-permission android:name="android.permission.RECEIVE_SMS"/>
    <uses-permission android:name="android.permission.READ_SMS" />
    

    三、短信的Uri共有一下几种:

    content://sms/inbox 收件箱
    content://sms/sent 已发送
    content://sms/draft 草稿
    content://sms/outbox 发件箱 (正在发送的信息)
    content://sms/failed 发送失败
    content://sms/queued 待发送列表 (比如开启飞行模式后,该短信就在待发送列表里)

    相关文章

      网友评论

      • yuyu000:在魅族手机上 , 无法监听到短信变化 。 广播和ContentObserver 均无法监听 (华为和模拟器可以监听)。
        大佬可知如何解决?
      • 9a996ce6ed8d:我始终觉得能开放第三方读取短信就会存在安全隐患
        唠嗑008:@双开门监听用的是广播,获取短信内容,也就是系统内置的数据库是用的ContentResolver!
        dc36981ec4e1:系统的短信信息也是可以截取的 也是广播
        唠嗑008: @blackforever 那也是没有办法,如果自己做,需要运营商接口。小公司没那么多资源,只能第三方,不是所有公司都想腾讯,阿里这么土豪的
      • 131a3b2a8eac:用Python写的?
        唠嗑008:@全村人的666 不是

      本文标题:Android短信验证码自动填充

      本文链接:https://www.haomeiwen.com/subject/zrzebttx.html