Android开发 亲测可用--多种方式获取手机短信验证码自动填入

2023-10-31

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);
    }

参考文章:全网文章。查了全网基本都没有解决回调和筛选重复的问题,我这个请参考。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android开发 亲测可用--多种方式获取手机短信验证码自动填入 的相关文章

  • 如何创建在 React-Native 中检测自动位置的地图

    我已经在react native中创建了地图 参考https github com lelandrichardson react native maps https github com lelandrichardson react nat
  • 如何制作像 Google+ 应用程序中那样的气泡? [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 有谁知道如何使用 Google An
  • 在工具栏下显示内容

    您好 我试图简单地将我的内容放在工具栏下方 但是当我运行我的应用程序时 某些内容本应位于工具栏下方 却隐藏在工具栏后面 我已经阅读了有关使用框架布局来尝试将其分离的内容 但我有点卡住了 我目前正在使用该软件提供的基本 android stu
  • 无法在 Android Studio 中导出签名的 APK

    当我使用keytool list keystore path to keyfile jks并提供我的密码 我可以看到那里的条目 但是当我在尝试使用相同的密码生成签名的 APK 时使用相同的密码时 我收到错误 无法加载密钥库 密钥库被篡改 或
  • 如何在不使用 Firebase 控制台的情况下发送 Firebase 云消息通知?

    我从新的 Google 通知服务开始 Firebase Cloud Messaging 感谢这段代码https github com firebase quickstart android tree master messaging htt
  • CoordinatorLayout 和 ImageView 在滚动时调整宽度的问题

    我正在尝试放置一个ImageView in a CollapsingToolbarLayout它在加载时占据整个屏幕 并且当您滚动内容时 16x9 分辨率图像宽度会调整大小 直到图像占据屏幕的整个宽度 那时 我希望图像具有视差app lay
  • 如何使用 RecyclerView 创建此布局?

    我正在尝试使用这种类型的布局创建回收器视图 这些项目是字符串 可以以不同的大小出现 我不知道每行中有多少项目 我可以使用 StaggeredGridLayoutManager 来做到这一点吗 该图像只是一个假示例 每行可以有更多项目 您可能
  • 如何在Android中的DataBinding的ViewModel类中获取R.string

    我目前正在使用databinding对于我的 Android 应用程序项目 我想设置error留言在我的CustomTextView from R string txtOldPassWordError并从另一个名为的类中进行设置ViewMo
  • Play 商店中的应用描述更新

    我想更新应用程序的描述以及 Play 商店上的屏幕截图 但应用程序保持相同 即相同的版本号 我不想发布新应用程序 因为应用程序中没有任何更改 这可能吗 谷歌也会要求更新应用程序吗 您可以更新描述 也可以更改屏幕截图 您的应用程序将保持不变
  • 如何使用 Google 的 GithubBrowserSample 方法在片段之间共享视图模型?

    我对 Android 架构组件的使用非常陌生 因此我决定使用 GithubBrowserSample 来构建我的应用程序来实现我的许多用例 但我有一个问题 我不知道使用这种方法在片段之间共享视图模型的正确方法是什么 我想共享视图模型 因为我
  • 什么是 Android 测试协调器?

    谷歌最近发布了Android测试支持库1 0 读完后overview https android developers googleblog com 2017 07 android testing support library 10 is
  • 在 Android 中加密/解密字符串的简单方法

    我的问题是如何加密String String AndroidId Override public void onCreate Bundle savedInstanceState super onCreate savedInstanceSta
  • 如何在 60 分钟后删除共享首选项

    我想存储登录数据 但希望在 60 分钟后删除该数据 执行此操作的正确方法是什么 在这 60 分钟内可以关闭 停止 打开应用程序 我不想使用内部数据库 这是我的访问代码SharedPreferences sharedpreferences g
  • 如何在Android模拟器中隐藏应用程序图标?

    我有一个应用程序在启动完成后自动启动 但应用程序图标显示在android模拟器中 现在我想向用户隐藏该图标 这样用户就无法知道应用程序已启动 并且他们无法启动该应用程序手动申请 在您的 AndroidManifest xml 文件中 您可能
  • 活动加载缓慢

    我的应用程序中有大约 50 个活动 但其中一个活动非常慢 我的意思是当我按下按钮进入该活动时 需要很长时间 有时会出现黑屏 在该活动中 我有一个按钮 如果我单击该按钮 则需要很长时间才能执行 代码很轻 只有一个Button EditText
  • 使 Recyclerview 固定高度并可滚动

    已解决以下检查答案 所以我试图为我的 Android 应用程序创建评论功能 我想在 recyclerview 中显示评论 然后在 recyclerview 下方有一个按钮和文本视图来添加评论 我想让 recyclerview 具有一定的高度
  • 如何知道我的应用程序的活动计数?

    我的应用程序中有多项活动 请考虑以下案例 Activity A 调用 Activity B B 调用 C 当用户按后退键时 C 生成通知并转到 B 现在再次按后退 因此将显示 A 再次按后退键也会终止 A 并显示通知 现在 当用户按下通知图
  • onPrepareOptionsMenu 复制 ActionBar 中的项目

    当我使用 onPrepareOptionsMenu 添加菜单项时 该菜单项会在操作栏中复制其自身 我正在使用片段并在主要活动的 ActionBar 中创建初始菜单 如下所示 Override public boolean onCreateO
  • Android 从通知刷新 Activity

    我有一个程序 我在其中调用通知 如果您将其下拉 该通知将启动一个新活动 mNotificationManager NotificationManager getSystemService ns int icon R drawable sta
  • Android 的代码覆盖率[重复]

    这个问题在这里已经有答案了 可能的重复 Android测试代码覆盖率 Eclipse https stackoverflow com questions 3282702 android test code coverage eclipse

随机推荐