Android开发 静态注册、动态注册、短信中心库监控获取手机验证码,自动复制到剪切板或或填入输入框。
友情提醒初学者:这是广播接收器的类,写在xml中静态注册或写在启动类的Oncreate方法下动态注册即可!有新短信通知就会触发。若使用正常的验证码填入功能,请处理完毕后在界面销毁处注销监听。第一种方式:广播接收者 静/动注册监听短信广播 获取验证码
第二种方式:内容观察者 观察短信库的变化,根据日期获取最新短信,并解决onChange回调多次的情况。
一.通过广播接收者监控获取验证码
1.SMSReceiver.java 类代码 - -静态、动态都可用
import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.telephony.SmsMessage;
import android.util.Log;
import android.widget.Toast;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SMSReceiver extends BroadcastReceiver {
private static final String TAG = "SMSReceiver";
@Override
public void onReceive(Context context, Intent intent) {
//调用短信内容获取类
getMsg(context, intent);
}
/**
* 短信内容的获取
* @param context
* @param intent
*/
private void getMsg(Context context, Intent intent) {
//解析短信内容 pdus短信单位pdu
if (intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")) {
Object[] pdus = (Object[]) intent.getExtras().get("pdus");
assert pdus != null;
for (Object pdu : pdus) {
//封装短信参数的对象
SmsMessage sms = SmsMessage.createFromPdu((byte[]) pdu);
String number = sms.getOriginatingAddress(); //获取发送短信的手机号
String body = sms.getMessageBody(); //获取接收短信手机号的完整信息
System.out.println("发短信的手机号:"+number+" "+"监听到的短信息:" + body);
//写处理逻辑 可以加线程
new Thread(new Runnable() {
@Override
public void run() {
try {
//在这里写线程相关的处理逻辑
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
//写处理逻辑,调用相关类 匹配获取验证码并复制到剪贴板
getCode(context, body);
}
}
}
/**
* 匹配出验证码并复制到剪贴板
* @param context
* @param body
*/
private void getCode(Context context, String body) {
//获取剪贴板管理器:
ClipboardManager cm = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
Pattern pattern1 = Pattern.compile("(\\d{4,6})");//正则匹配4-6位数字
Matcher matcher1 = pattern1.matcher(body);//进行匹配
if (matcher1.find()) {//匹配成功
String code = matcher1.group(0);
// 创建普通字符型ClipData
ClipData mClipData = ClipData.newPlainText("Label", code);
// 将mClipData内容放到手机系统剪贴板
cm.setPrimaryClip(mClipData);
Toast.makeText(context, "验证码复制成功,请粘贴", Toast.LENGTH_SHORT).show();
Log.d(TAG, "onReceive: " + code);
} else {
Toast.makeText(context, "未检测到验证码,请手动输入", Toast.LENGTH_SHORT).show();
Log.d(TAG, "onReceive: " + "未检测到验证码");
}
}
}
2.动态申请权限的相关类
在项目代码中的目标获取验证码处,动态申请读取短信相关权限的代码,也可在进入软件后界面的Oncreate下搞代码,动态申请权限。强制用户同意存储及短信权限。
2.1XML中添加存储读写及短信权限
<!-- 修改或删除您的USB存储设备中的内容 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!--发送及接收读取短信的权限-->
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
2.2在合适/启动界面申请获取权限
public class LoginActivity extends Activity {
//日志打印常量
private String TAG;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//在onCreate方法这里调用来动态获取存储权限以及短信权限
getReadPermissions();
}
/**
* 权限的验证及处理,相关方法
*/
private void getReadPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_SMS) != PackageManager.PERMISSION_GRANTED
| ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECEIVE_SMS) | ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {//是否请求过该权限
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.RECEIVE_SMS,
Manifest.permission.READ_SMS,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE}, 10001);
} else {//没有则请求获取权限,示例权限是:存储权限和短信权限,需要其他权限请更改或者替换
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.RECEIVE_SMS,
Manifest.permission.READ_SMS,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE}, 10001);
}
} else {//如果已经获取到了权限则直接进行下一步操作
Log.e(TAG, "onRequestPermissionsResult");
}
}
}
/**
* 一个或多个权限请求结果回调
* 当点击了不在询问,但是想要实现某个功能,必须要用到权限,可以提示用户,引导用户去设置
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 10001:
for (int i = 0; i < grantResults.length; i++) {
// 如果拒绝获取权限
if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
//判断是否勾选禁止后不再询问
boolean flag = ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[i]);
if (flag) {
getReadPermissions();
return;//用户权限是一个一个的请求的,只要有拒绝,剩下的请求就可以停止,再次请求打开权限了
} else { // 勾选不再询问,并拒绝
Toast.makeText(this, "请到设置中打开权限", Toast.LENGTH_LONG).show();
return;
}
}
}
// Toast.makeText(LoginActivity.this, "权限开启完成",Toast.LENGTH_LONG).show();
break;
default:
break;
}
}
3.静态注册广播接收者
3.1AndroidManifest.xml添加权限及广播服务
<!--发送接收读取短信的权限 都加上吧-->
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<application
android:allowBackup="true"
android:hardwareAccelerated="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:largeHeap="true"
android:theme="@style/AppTheme">
。。。。。。。。
<receiver
android:name=".SMSReceiver这里是静态注册写法,写广播类所在的位置">
<intent-filter android:priority="1000">
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
。。。。。。。。
</application>
4.动态注册广播接收者
4.1在启动界面动态注册
public class LoginActivity extends Activity {
//日志打印常量
private String TAG;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//动态注册广播接收者且设置为最大优先级
IntentFilter filter = new IntentFilter();
filter.addAction("android.provider.Telephony.SMS_RECEIVED" );
filter.setPriority(Integer.MAX_VALUE);//设置动态优先级为最大,1000应该不是最大
SMSReceiver receiver = new SMSReceiver();
registerReceiver(receiver,filter);
}
二.通过内容监控者读取短信库获取验证码
1.SMSobserver.java 类代码
解决SMSobserver的onChang回调或获取短信不是最新,以及获取重复短信问题。
import android.content.ClipboardManager;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.util.Log;
import android.widget.Toast;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SMSobserver extends ContentObserver {
private Context mContext;
private Handler mHandler;
private String TAG;
private static int id=0; //这里必须用静态的,防止程序多次意外初始化情况
public SMSobserver(Context context, Handler handler) {
super(handler);
mContext = context;
mHandler = handler;
}
@Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
//过滤可能界面调用初始化两次的情况
if (uri.toString().contains("content://sms/raw")) {
return;
}
Uri inboxUri = Uri.parse("content://sms/inbox");
Cursor cursor = mContext.getContentResolver().query(inboxUri, null, null, null, "date desc");
if (cursor != null) {
if (cursor.moveToFirst()) {
String _id = cursor.getString(cursor.getColumnIndex("_id"));
//比较id 解决重复问题
if (id < Integer.parseInt(_id)) {
id = Integer.parseInt(_id);//将获取到的当前id记录,防止重复
String address = cursor.getString(cursor.getColumnIndex("address"));
String body = cursor.getString(cursor.getColumnIndex("body"));
String date = cursor.getString(cursor.getColumnIndex("date"));
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String time1 = dateFormat.format(new Date(Long.parseLong(date)));
//写自己的处理逻辑,我这里是复制验证码到剪切板
if(body.contains("短信中包含某字符才匹配复制到剪切板")) {
Pattern pattern = Pattern.compile("(\\d{4,6})");
Matcher matcher = pattern.matcher(body);
if (matcher.find()) {
String code = matcher.group(0);
System.out.println("验证码:" + code);
ClipboardManager cmb = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
cmb.setText(code);
Toast.makeText(mContext.getApplicationContext(), "复制成功" + code, Toast.LENGTH_SHORT).show();
}
}
}
}
cursor.close();
}
}
}
2.调用注册内容监控者
public class LoginActivity extends Activity {
//回调显示
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == 1) {
String code = (String) msg.obj;
editname.setText(code);//要显示验证码的控件id
}
}
};
//防止多次初始化
private SMSobserver smsobserver = new SMSobserver(this, mHandler);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//调用注册内容监控者
getContentResolver().registerContentObserver(Uri.parse("content://sms"),true, smsobserver);
}
参考文章:全网文章。查了全网基本都没有解决回调和筛选重复的问题,我这个请参考。