获取Android短信验证码小工具

日常笔记

注:该功能被封装成SDK,主要是为游戏或应用的登录功能服务,减少玩家记验证码这一步繁琐步骤,各位小伙伴请谨慎应用读取短信的技术哦!谨记!谨记!谨记!重要事情说三遍。

如有写的不好的地方,欢迎指出,共勉,谢谢!

开发环境:android studio;功能:读取短信验证码;应用:unity游戏。

这里我拆分为三部分:1、权限;2、广播;3、正则表达式。

一、权限:

在接口类写权限申请、广播注册和广播注销逻辑。ContetxCompat.checkSelfPermission(Context context, String permission)检查用户是否已授权某一权限。ActivityCompat.requestPermissions(Activity activity, String[] permissions, int requestCode)请求权限。

具体的代码如下:

```java

/**

    * * 检查申请短信权限

    * */

    private static void checkSMSPermission() {

        String[] permissions = new String[]{Manifest.permission.READ_SMS, Manifest.permission.RECEIVE_SMS};

        //用于存放授权权限

        List permissionList = new ArrayList<>();

        //遍历传递过来的权限集合

        for (String permission : permissions) {

            if (ContextCompat.checkSelfPermission(mContext, permission)  != PackageManager.PERMISSION_GRANTED) {

                Log.d("sms", "Permission not obtained");

                //未授权,则加入待授权的权限集合中

                permissionList.add(permission);

            } else

            {

                Log.d("sms", "Permission obtained");

            }

        }

        //判断集合

        if (!permissionList.isEmpty()){  //如果集合不为空,则需要去授权

            Logger.d("ask permission");

            ActivityCompat.requestPermissions(mActivity, permissionList.toArray(new String[permissionList.size()]),1);

        }

    }

```

ps:用户点击“禁止后不再提示”,系统将不再弹权限申请窗口,而这个权限是必须的话,那么应该怎么去引导用户权限呢?可以写一个弹窗来解释权限的用途,用户同意开启权限就跳到设置让用户手动开启权限。安卓提供了一个返回布尔值的方法,来判断是否还可以申请权限 boolean b = activity.shouldShowRequestPermissionRationale(String permission);。 b默认值为false;点击“禁止”,b为true;点击“禁止后不再提示”,b为false。判断逻辑在onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults)(权限请求结果回调方法)方法里实现。

二、广播:

笔者这里写了一个配置类来管理配置:

```java

/**

* @author rs

* @date 2020/07/01

* */

public class SMSConfig {

    public long verificationCodeDigit;

    public String verificationCodeIdentifier;

    // 构造器

    //SMSCofig是自定义配置类,笔者的SMSConfig类的属性有验证码位数和短信格式(也就是正则表达式)

    public SMSConfig(long verificationCodeDigit, String verificationCodeIdentifier ) {

        this.verificationCodeDigit = verificationCodeDigit;

        this.verificationCodeIdentifier = verificationCodeIdentifier;

    }

}

```

写一个继承了BroadcastReceiver 的子类:

```java

public class SMSReceiver extends BroadcastReceiver {

    private static SMSListener smsListener;

    private static long verificationCodeDigit;

    private static String verificationCodeIdentifier;

    private static String phoneNumber;

    private static String body;

    public static void init(SMSConfig messageConfig, SMSListener listener){

        verificationCodeDigit = messageConfig.verificationCodeDigit;

        verificationCodeIdentifier = messageConfig.verificationCodeIdentifier;

        smsListener = listener;

    }

    @Override

    public void onReceive(Context context, Intent intent) {

        Log.d("sms", "currentTime: "+ System.currentTimeMillis());

        getMsg(intent);

    }

}

```

然后在申请权限类里动态注册广播:

```java

intentFilter = new IntentFilter();

        intentFilter.addAction("android.provider.Telephony.SMS_RECEIVED");

        //setPriority “优先级”值,该值用于对多个匹配过滤器进行排序。

        intentFilter.setPriority(1000);

        smsReceiver = new SMSReceiver();

        smsReceiver.init(messageConfig, listener);

        mActivity.registerReceiver(smsReceiver, intentFilter);

        isRegisterReceiver = true;

```

注销广播:

```java

//注销监听

    public static void destroy(){

        if (!isRegisterReceiver){

            return;

        }

        if (mActivity != null && smsReceiver != null){

            mActivity.unregisterReceiver(smsReceiver);

            isRegisterReceiver = false;

            Logger.d("广播注销成功");

        }

    }

```

三、解析短信:

```java

private static void getMsg(Intent intent){

        body = "";

        //pdus短信单位pdu

        //解析短信内容

        Object[] pdus = (Object[]) intent.getExtras().get("pdus");

        assert pdus != null;

        for (Object pdu : pdus) {

            //封装短信参数的对象

            SmsMessage sms = SmsMessage.createFromPdu((byte[]) pdu);

            phoneNumber = sms.getOriginatingAddress();

            body  += sms.getMessageBody();//一次解析出来的短信内容有限,需要拼接成完成的短信。

        }

        Log.d("sms","phoneNumber: "+phoneNumber+ " body: "+ body);

        //写自己的处理逻辑

        //获取短信验证码

        getCode(body);

    }

```

四、正则表达式:

正表达式的详细学习可以查看:https://www.runoob.com/java/java-regular-expressions.html 网站。下面代码有注释,笔者就不在这里啰嗦了。

```java

private static void getCode(String body){

        Pattern pattern = Pattern.compile("(?

        Matcher matcher = pattern.matcher(body);//尝试将整个区域与模式匹配。

        Pattern pattern1 = Pattern.compile(verificationCodeIdentifier);//pattern 对象是一个正则表达式的编译表示。verificationCodeIdentifier是正则表达式。

        Matcher matcher1 = pattern1.matcher(body);//尝试将整个区域与模式匹配。

        boolean isIdentifier = matcher1.find();//尝试查找与该模式匹配的输入序列的下一个子序列。

        Logger.d("verificationCodeIdentifier: "+verificationCodeIdentifier+" isIdentifier: "+isIdentifier + " body: " + body);

        //匹配验证码失败

        if (!matcher.find()){

            String msg = "No verification code detected.";

            if (smsListener != null){

                smsListener.onFailed(1001, msg);

            }

            Log.d("sms", "onReceive code: " + msg);

            return;

        }

        //匹配文字失败

        if (!isIdentifier){

            Log.d("sms", "This message does not have the required identifier.");

            return;

        }

        String code = matcher.group(0);

        if (smsListener != null){

              smsListener.onSuccess(code);

        }

        Log.d("sms", "onReceive code: " + code);

    }

```

总结:

一、用户点击“禁止后不再提示”,系统将不再弹权限申请窗口,笔者想根据onRequestPermissionsResult()方法的回调结果,来判断是否弹出提示窗口,在这操作上花费了很多时间,原因是笔者一直想在非activity类里直接能获取onRequestPermissionsResult()方法的回调结果,网上看了很多博客,到头来都是无果。最后想直接使用shouldShowRequestPermissionRationale(String permission)的特性来实现弹出提示窗口,逻辑:实例化一个布尔值boolean,并存放在本地,默认值是false,仅当shouldShowRequestPermissionRationale(String permission)为true时boolean的值才会被修改,boolean值为true,当boolean为true时才判断权限是否可用。显然这种写法并不好,缺点:1、多了读写权限,2、跳开系统源码提供的方法。如果读者有解决这个问题的方法,请教导教导笔者一下可好?二、面向对象问题,笔者有一个不好的习惯,习惯性的根据功能直接上手码代码,脱离了java的面向对象。比如:笔者一开始是没有写配置类的,而是写了一个map来代替,其实这些参数是验证码短信的一些参数,可以理解为短信有这些参数才是完整的,没理由脱离sms,以map数据结构来记录。这个毛病记录在此,提醒自己不能只码代码,脱离理论,不懂思考。

以上完,希望能对读者有帮助。

(冲!冲!冲!)

你可能感兴趣的:(获取Android短信验证码小工具)