Android 开发: 实现 APP 自动填写注册验证码功能

2,924 阅读4分钟

前言

此技术的实现原理是“广播接收者”和“内容观察者”。
利用接收到短信时的系统发出的广播进行短信的读取,并利用正则表达式解析出短信中的验证码。
在部分手机上,该广播被禁用,所以需要使用到内容观察者,在短信收件箱发生变化时,对短信数据库进行读取,得到相应验证码短信,并利用正则表达式解析出短信中的验证码。本文内容请使用于正规用途,请勿用于非法监听用户短信等恶意行为。谢谢合作!

本文原创作者xiong_it,本文原创链接:blog.csdn.net/Xiong_IT/ar…

本篇文章已授权微信公众号 guolin_blog(郭霖)独家发布.


使用广播接收者获取验证码

  • 广播接收者一般用法

    1. 继承BroadcastReceiver
    2. 重写onReceive(Context context, Intent intent);
    3. 注册自定义广播接收者(1.代码动态注册;2.清单文件注册)

使用广播接收者获取验证码实现代码如下:

public class ReadSmsCodeReceiver extends BroadcastReceiver {
    private static final String SMS_RECEIVED_ACTION =Telephony.Sms.Intents.SMS_RECEIVED_ACTION;// 接收到短信时的action

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(SMS_RECEIVED_ACTION)) {
            getSmsCodeFromReceiver(intent);
        }
    }

    /**
     * 从接收者中得到短信验证码
     *
     * @param intent
     */
    private void getSmsCodeFromReceiver(Intent intent) {
        KJLoger.log(TAG, "getSmsCodeFromReceiver");
        SmsMessage[] messages = null;
        if (Build.VERSION.SDK_INT >= 19) {
            messages = android.provider.Telephony.Sms.Intents.getMessagesFromIntent(intent);
            if (messages == null) return;
        } else {
            messages = getSmsUnder19(intent);
            if (messages == null) return;
        }

        if (messages.length > 0) {
            SmsMessage sms = messages[0];
            // 短信发送人
            String smsSender = sms.getOriginatingAddress();
            // 短信内容
            String smsBody = sms.getMessageBody();

            // 检查短信发送者,和短信内容
            if (checkSmsSender(smsSender) && checkSmsBody(smsBody)) {
                // 解析短信内容,得到验证码
                String smsCode = parseSmsBody(smsBody);
                // 自定义的方法,用来发送验证码给Handler,进行填写
                sendMsg2Register(OBSERVER_SMS_CODE_MSG, smsCode);
            }
        }
    }

    /**
     * 当API低于19时的做法
     * @param intent
     * @return 短信实例
     */
    private SmsMessage[] getSmsUnder19(Intent intent) {
        SmsMessage[] messages;
        Bundle bundle = intent.getExtras();
        // 相关链接:https://developer.android.com/reference/android/provider/Telephony.Sms.Intents.html#SMS_DELIVER_ACTION
        Object[] pdus = (Object[]) bundle.get("pdus");

        if ((pdus == null) || (pdus.length == 0)) {
            return null;
        }

        messages = new SmsMessage[pdus.length];
        for (int i = 0; i < pdus.length; i++) {
            if (Build.VERSION.SDK_INT >= 23) {
                String format = bundle.getString("format");
                messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i], format);
            } else {
                messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
            }
        }
        return messages;
    }
}

以上,从短信广播中得到了Message实例,并得到短信。验证码获取方法为parseSmsBody(String smsBody),最后会统一讲解。

使用内容观察者得到短信验证码

  • 内容观察者的一般用法

    1. 继承内容观察者ContentObserver
    2. 重写onChange(boolean selfChange)方法
    3. 注册内容观察者
context.getContentResolver().registerContentObserver(uri, true, mReadSmsObserver);

使用内容观察者获取验证码实现代码如下:

public class ReadSmsObserver extends ContentObserver {
        private static final String SMS_INBOX_URI = "content://sms/inbox";//API level>=23,可直接使用Telephony.Sms.Inbox.CONTENT_URI,用于获取cusor
        private static final String SMS_URI = "content://sms";//API level>=23,可直接使用Telephony.Sms.CONTENT_URI,用于注册内容观察者
        private static final String[] PROJECTION = new String[]{
                Telephony.Sms._ID,
                Telephony.Sms.ADDRESS,
                Telephony.Sms.BODY,
                Telephony.Sms.DATE
        };
        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);
            // 读取短信收件箱,只读取未读短信,即read=0,并按照默认排序
            Cursor cursor = getContentResolver().query(Uri.parse(SMS_INBOX_URI), PROJECTION,
                    Telephony.Sms.READ + "=?", new String[]{"0"}, Telephony.Sms.Inbox.DEFAULT_SORT_ORDER);
            if (cursor == null) return;

            while (cursor.moveToNext()) {
                // 读取短信发送人
                String address = cursor.getString(cursor.getColumnIndex(Telephony.Sms.ADDRESS));
                // 读取短息内容
                String smsBody = cursor.getString(cursor.getColumnIndex(Telephony.Sms.BODY));
                // 检查短信发送者,和短信内容
                if (checkSmsSender(address) && checkSmsBody(smsBody)) {
                    // 解析短信内容,得到验证码
                    String smsCode = parseSmsBody(smsBody);
                    // 自定义的方法,用来发送验证码给Handler,进行填写
                    sendMsg2Register(RECEIVER_SMS_CODE_MSG, smsCode);
                    // 关闭cursor的方法
                    closeCursor(cursor);
                    return;
                }
            }
            // 关闭cursor的方法
            closeCursor(cursor);
        }
    }

注意:注册短信观察者的Uri和获取Cursor的Uri不相同,如果使用获取Cursor的Uri进行注册,则得不到onChange回调。

正则表达式解析短信得到验证码

    /**
     * 解析短信得到验证码
     *
     * @param smsBody
     * @return 验证码
     */
    private String parseSmsBody(String smsBody) {
        String regex = new String("(\\d{4})");// 匹配规则为短信中的连续4位数字
        String smsCode = "";// 验证码

        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(smsBody);

        while (matcher.find()) {// 如果匹配到了
            smsCode = matcher.group(0);
        }
        Log.w(TAG, "smsCode = " + smsCode);
        return smsCode;
    }

配置权限

    <!--用于读取短信验证码,缺一不可-->
    <uses-permission android:name="android.permission.READ_SMS" />
    <uses-permission android:name="android.permission.RECEIVE_SMS" />

多说一句:笔者当时只配置了Android.permission.READ_SMS权限,结果定义的短信广播接收者死活不回调onReceive(Context context, Intent intent);

本文原创作者xiong_it,本文原创链接:blog.csdn.net/Xiong_IT/ar…

填写验证码

将上述从广播接收者,内容观察者中得到的smsCode填入EditText输入框中即可。

笔者开源了一个AutoInputAuthCode类库,用于帮助开发者快速实现该功能,博客地址:blog.csdn.net/xiong_it/ar…

Github:github.com/xiong-it/Au…

参考链接

Java正则表达式教材:www.runoob.com/java/java-r…
Android短信说明:developer.android.com/reference/a…