广告位联系
返回顶部
分享到

Android实现获取短信验证码并自动填充

Android 来源:互联网 作者:佚名 发布时间:2023-08-10 22:44:19 人浏览
摘要

最近弄了个短信自动填充功能,一开始觉得很简单,不就是动态注册个广播接收器去监听短信消息不就可以了吗?结果没这么简单,问题就出在机型的适配上。小米的短信权限、荣耀、

最近弄了个短信自动填充功能,一开始觉得很简单,不就是动态注册个广播接收器去监听短信消息不就可以了吗?结果没这么简单,问题就出在机型的适配上。小米的短信权限、荣耀、OPPO的短信监听都是坑,暂时就用这三个手机测了,其他的遇到了再补充。

下面简单讲讲:

权限

申请权限

短信属于隐私权限,Android 6.0后需要动态申请权限。首先在manifest里面注册权限:

1

2

<uses-permission android:name="android.permission.RECEIVE_SMS" />

<uses-permission android:name="android.permission.READ_SMS" />

在需要用的地方,动态申请下:

1

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

小米短信权限问题

本来这样权限问题就搞定了,但是在小米手机上就不行。小米手机会把短信归类到通知类权限里:

在 ContextCompat.checkSelfPermission 的时候会直接返回true,并且不会弹出权限对话框,而是在实际使用的时候才会咨询用户,按理说好像和我们逻辑没有冲突,但是在使用receiver进行监听前,不是得确保有权限么?实际效果也是,在没有权限时,不能获取到短信的广播。

小米短信权限解决

在网上找了找办法,好像也没多少博文,但是大致有了思路:不是用的时候才申请么?那我就先用一下,再去用receiver监听。下面是方法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

// 读取一下试试,能读取到就有权限

boolean flag = false;

try {

    Uri uri = Uri.parse("content://sms/inbox");

    ContentResolver cr = context.getContentResolver();

    String[] projection = new String[]{"_id"};

    Cursor cur = cr.query(uri, projection, null, null, "date desc");

    if (null != cur) {

        cur.close();

    }

    lag = true;

}catch (Exception e) {

    e.printStackTrace();

}

这里仅针对小米手机啊,对小米手机的判断我只是用 android.os.Build.MANUFACTURER 简单判断了下,如果有更高要求请查找资料。

使用Receiver进行监听

编写SmsReceiver

这里也是网上随便找了个代码,能用,但是在荣耀手机上却是偶尔能收到一次,后面几次就收不到了,打了log也没进入到onReceive中,这就很离奇了,排查了很久。同样的代码,在小米手机上是没问题的,那就只可能是适配问题了。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

import android.content.BroadcastReceiver;

import android.content.Context;

import android.content.Intent;

import android.os.Bundle;

import android.telephony.SmsMessage;

import android.util.Log;

 

public class SmsReceiver extends BroadcastReceiver {

 

    @Override

    public void onReceive(Context context, Intent intent) {

        //Toast.makeText(context, "收到信息", Toast.LENGTH_LONG).show();

        Log.d("SmsReceiver", "onReceive: " + intent.getAction());

        if(intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")){

            //intent.getExtras()方法就是从过滤后的意图中获取携带的数据,

            // 这里携带的是以“pdus”为key、短信内容为value的键值对

            // android设备接收到的SMS是pdu形式的

            Bundle bundle = intent.getExtras();

            SmsMessage msg;

            if (null != bundle){

                //生成一个数组,将短信内容赋值进去

                Object[] smsObg = (Object[]) bundle.get("pdus");

                //遍历pdus数组,将每一次访问得到的数据方法object中

                for (Object object:smsObg){

                    //获取短信

                    msg = SmsMessage.createFromPdu((byte[])object);

                    //获取短信内容

                    String content = msg.getDisplayMessageBody();

                    Log.d("SmsReceiver", "onReceive: content = " + content);

                    //获取短信发送方地址

                    String from = msg.getOriginatingAddress();

                    Log.d("SmsReceiver", "onReceive: from = " + from);

 

                    // TODO ...

                }

            }

        }

    }

}

使用方法:

1

2

3

4

5

6

7

8

9

// 使用广播进行监听

IntentFilter smsFilter = new IntentFilter();

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

smsFilter.addAction("android.provider.Telephony.SMS_DELIVER");

if (smsReceiver == null) {

    smsReceiver = new SmsReceiver();

}

smsReceiver.setCallbackContext(callbackContext);

context.registerReceiver(smsReceiver, smsFilter);

接触监听,最好在收到短信的时候就取消注册广播:

1

context.unregisterReceiver(smsReceiver);

解决OPPO手机无法接收短信广播问题

本来小米荣耀都搞定了,给测试一测,结果又不行了。收不到广播,用下面的ContentObserver还总拿不到对的数据。找了下资料,发现OPPO手机需要在短信APP进行设置。

ps. 后面发现华为、荣耀都是这样,会对验证码进行保护。可以使用ContentObserver 监听,能触发onChange,但是拿不到Uri,不过可以使用查询,拿到倒叙的第一条数据,取出其中的date属性,比对监听时的时间,如果短信两分钟有效,那就看看第一条数据是不是在两分钟内,如果不是,那就是没拿到,问题就出在用户开启了短信验证码保护,可以提示用户自行输入验证码(毕竟这个不是我们的锅)。

解决方法: 在短信 -> 短信设置里面 -> 打开禁止后台应用读取验证码

解决荣耀无法连续监听短信的问题

既然上面的方法没用了,只能找新的办法喽,网上很多提供了两种办法,第二种就是通过ContentResolver去监听短信添加的更新动作,其实也和广播类似,代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

import android.content.Context;

import android.database.ContentObserver;

import android.database.Cursor;

import android.net.Uri;

import android.os.Build;

import android.os.Handler;

import android.provider.Telephony;

import android.util.Log;

 

import androidx.annotation.RequiresApi;

 

@RequiresApi(api = Build.VERSION_CODES.KITKAT)

public class ReadSmsObserver extends ContentObserver {

 

    private final Context context;

 

    public ReadSmsObserver(Handler handler, Context context) {

        super(handler);

        this.context = context;

    }

 

    private static final String SMS_INBOX_URI = "content://sms/inbox";//API level>=23,可直接使用Telephony.Sms.Inbox.CONTENT_URI,用于获取cusor

    // private static final String SMS_URI = "content://sms";//API level>=23,可直接使用Telephony.Sms.CONTENT_URI,用于注册内容观察者

    private static final String[] PROJECTION = new String[]{

            Telephony.Sms._ID,

            Telephony.Sms.ADDRESS,

            Telephony.Sms.BODY,

            Telephony.Sms.DATE

    };

 

    @Override

    public void onChange(boolean selfChange, Uri uri) {

        super.onChange(selfChange);

        Log.d("ReadSmsObserver", "onChange: ");

        // 当收到短信时调用一次,当短信显示到屏幕上时又调用一次,所以需要return掉一次调用

        if(uri.toString().equals("content://sms/raw")){

            return;

        }

        // 读取短信收件箱,只读取未读短信,即read=0,并按照默认排序

        Cursor cursor = context.getContentResolver().query(Uri.parse(SMS_INBOX_URI), PROJECTION,

                Telephony.Sms.READ + "=?", new String[]{"0"}, Telephony.Sms.Inbox.DEFAULT_SORT_ORDER);

        if (cursor == null) return;

        // 获取倒序的第一条短信

        if (cursor.moveToFirst()) {

            // 读取短信发送人

            String address = cursor.getString(cursor.getColumnIndex(Telephony.Sms.ADDRESS));

            Log.d("ReadSmsObserver", "onChange: address = " + address);

            // 读取短息内容

            String smsBody = cursor.getString(cursor.getColumnIndex(Telephony.Sms.BODY));

            Log.d("ReadSmsObserver", "onChange: smsBody = " + smsBody);

             

            // TODO 传递出去,最好切下线程

 

        }

        // 关闭cursor的方法

        cursor.close();

    }

}

用的时候要注册和取消注册:

1

2

3

4

5

6

7

8

9

// 使用ContentResolver进行监听

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

    if (smsObserver == null) {

        smsObserver = new ReadSmsObserver(new SmsHandler(), context);

    }

    smsObserver.setCallbackContext(callbackContext);

    context.getContentResolver().registerContentObserver(

        Uri.parse("content://sms/"), true, smsObserver);

}

取消注册:

1

context.getContentResolver().unregisterContentObserver(smsObserver);

解决OPPO手机无法拿到最新短信问题

很神奇啊,每次使用ContentObserver去监听短信变化,明明onChange触发了,但是去拿短信就是拿不到最新的,开了上面的设置也不行,弄了好久。

最后想的解决办法是,两种方式同时监听,在onChange触发后等待三秒钟(开始试了1s还不行),看看有没有onReceive,如果有就直接使用onReceive的短信,如果没有再验证onChange内拿到的短信,看看是不是有效时间内的,连倒叙第一个都在有效时间外,那就是没拿到了,直接舍弃了。

PS. 后续更新,感觉这些问题都可能是手机系统开了短信验证码保护。

思路是这样,做起来不麻烦,用个handler就可以解决,读者自行处理吧。


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 :
相关文章
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计