日常笔记
注:该功能被封装成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数据结构来记录。这个毛病记录在此,提醒自己不能只码代码,脱离理论,不懂思考。
以上完,希望能对读者有帮助。
(冲!冲!冲!)