获取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<String> 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("(?<![0-9])([0-9]{" + verificationCodeDigit+ "})(?![0- 9])");//提取六位/四位数字,pattern 对象是一个正则表达式的编译表示。

        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数据结构来记录。这个毛病记录在此,提醒自己不能只码代码,脱离理论,不懂思考。

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

(冲!冲!冲!)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容