App读取短信实现

应用中通常会涉及到读取短信,下面通过自动获取短信验证码演示了如何在应用中读取短信。

权限

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

通过广播监听短信

注意:这种方式只有在新接收到消息的时候才会执行代码,并不会读取收件箱中已读或未读的消息。

class SmsReceiver extends BroadcastReceiver {
    private OnSmsCatchListener mCallback;
    private String mPhoneNumber;
    private String mFilterRegex;

    public void setCallback(OnSmsCatchListener<String> callback) {
        this.mCallback = callback;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        final Bundle bundle = intent.getExtras();
        try {
            if (bundle != null) {
                final Object[] pdusObj = (Object[]) bundle.get("pdus");
                for (int i = 0; i < pdusObj.length; i++) {
                    SmsMessage currentMessage = getIncomingMessage(pdusObj[i], bundle);
                    String phoneNumber = currentMessage.getDisplayOriginatingAddress();

                    if (mPhoneNumber != null && !mPhoneNumber.equals(phoneNumber)) {
                        return;
                    }

                    String message = currentMessage.getDisplayMessageBody();
                    if (mFilterRegex != null && !message.matches(mFilterRegex)) {
                        return;
                    }

                    if (mCallback != null) {
                        mCallback.onSmsCatch(message);
                    }
                }
            }

        } catch (Exception e) {
        }
    }

    private SmsMessage getIncomingMessage(Object aObject, Bundle bundle) {
        SmsMessage currentSMS;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            String format = bundle.getString("format");
            currentSMS = SmsMessage.createFromPdu((byte[]) aObject, format);
        } else {
            currentSMS = SmsMessage.createFromPdu((byte[]) aObject);
        }
        return currentSMS;
    }


    public void setPhoneNumber(String phoneNumber) {
        this.mPhoneNumber = phoneNumber;
    }

    public void setFilterRegex(String regex) {
        this.mFilterRegex = regex;
    }

    public interface OnSmsCatchListener {
        void onSmsCatch(String message);
    }
}

接下来注册广播:

SmsReceiver receiver = new SmsReceiver();
receiver.setCallback(onSmsCatchListener);
receiver.setPhoneNumber(phoneNumber);
receiver.setFilterRegex(regex);
IntentFilter filter = new IntentFilter();
filter.addAction("android.provider.Telephony.SMS_RECEIVED");
activity.registerReceiver(receiver, filter);

最后在onStop/onDestroy中取消注册:activity.unregisterReceiver(receiver)。

注意:系统这个广播是有序广播,当其他应用程序先获取到了这个广播再传递给你,如果广播在其他应用程序中被注销了就接收不到了。通过设置priority的数值,其实有时是不管用的,现在在一些定制的系统或是有安全软件的情况下,短信往往都会被截取并注销掉广播监听。

通过ContentObserver监听短信

注意:这种方式可以获取手机上所有的短信,包括已读未读的短信

public class SmsObserver extends ContentObserver {

    private OnSmsCatchListener mCatchListener;
    private String mPhoneNumberFilter;
    private String mSmsCatchRegex;
    private WeakReference<Activity> mActivityRef;

    public void setCallback(OnSmsCatchListener listener) {
        this.mCatchListener = listener;
    }

    public SmsObserver(Activity activity, Handler handler) {
        super(handler);
        mActivityRef = new WeakReference<Activity>(activity);
    }

    @Override
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);

        if (null == mCatchListener || null == mActivityRef.get()) {
            return;
        }

        Cursor cursor = null;
        try {
            final Uri uri = Uri.parse("content://sms/");
            final String[] projection = new String[]{"address", "body"};
            cursor = mActivityRef.get().getContentResolver().query(uri, projection, "read=?", new String[]{"0"}, "date desc");
            if (cursor.moveToFirst()) {
                do {
                    final String address = cursor.getString(cursor.getColumnIndex("address"));
                    if (!TextUtils.isEmpty(mPhoneNumberFilter) && !mPhoneNumberFilter.equals(address)) {
                        Pattern p = Pattern.compile(mPhoneNumberFilter);
                        Matcher m = p.matcher(address);
                        if (!m.matches()) {
                            return;
                        }
                    }

                    final String message = cursor.getString(cursor.getColumnIndex("body"));
                    if (!TextUtils.isEmpty(mSmsCatchRegex)) {
                        Pattern p = Pattern.compile(mSmsCatchRegex);
                        Matcher m = p.matcher(message);
                        if (m.find()) {
                            mCatchListener.onSmsCatch(m.group(0));
                            return;
                        }
                    }
                } while (cursor.moveToNext());
            }
        } catch (Exception e) {
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    /**
     * 手机号码过滤
     *
     * @param filter 手机号或者手机号正则表达式
     */
    public void setPhoneNumberFilter(String filter) {
        this.mPhoneNumberFilter = filter;
    }

    public void setSmsCatchRegex(String regex) {
        this.mSmsCatchRegex = regex;
    }
}

然后通过getContentResolver().registerContentObserver(Uri.parse(“content://sms/”), true, mSmsObserver)注册监听。

最后通过getContentResolver().unregisterContentObserver(mSmsObserver)注销监听。

SMS结构

字段 描述 值域/例子
_id 短消息序号
thread_id 对话的序号
address 发件人地址,手机号
person 发件人,返回一个数字就是联系人列表里的序号,陌生人为空
date 消息接收日期,long型
date_sent 消息发送日期,long型
protocol 协议 SMS_RPOTO = 0
MMS_PROTO = 1
read 是否阅读 未读 = 0
已读 = 1
status 状态 STATUS_NONE = -1
STATUS_COMPLETE = 0
STATUS_PENDING = 64
STATUS_FAILED = 128
type 消息的类型 MESSAGE_TYPE_ALL = 0
MESSAGE_TYPE_INBOX = 1
MESSAGE_TYPE_SENT = 2
MESSAGE_TYPE_DRAFT = 3
MESSAGE_TYPE_OUTBOX = 4
MESSAGE_TYPE_FAILED = 5
MESSAGE_TYPE_QUEUED = 6
reply_path_present 消息上是否设置了TP-Reply-Path位
subject 消息的主题,如果存在
body 消息内容
service_center 短信服务中心号码编号 +8613800755500
locked 消息是否被锁定
error_code 与发送或接收此消息相关的错误代码
seen 指示该消息是否已被用户看到。该标志将用于确定是否需要弹出状态栏通知
timed
deleted
sync_state
marker
source
bind_id
mx_status
mx_id
out_time
account
sim_id
block_type
advanced_seen
b2c_ttl
b2c_numbers
fake_cell_type
url_risky_type

关于权限问题

android6.0(API23)以后读取短信权限必须在运行时申请。

感谢大家的支持,如有错误请指正,如需转载请标明原文出处!

你可能感兴趣的:(Android,android)