应用中通常会涉及到读取短信,下面通过自动获取短信验证码演示了如何在应用中读取短信。
<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的数值,其实有时是不管用的,现在在一些定制的系统或是有安全软件的情况下,短信往往都会被截取并注销掉广播监听。
注意:这种方式可以获取手机上所有的短信,包括已读未读的短信
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)注销监听。
字段 | 描述 | 值域/例子 |
---|---|---|
_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)以后读取短信权限必须在运行时申请。
感谢大家的支持,如有错误请指正,如需转载请标明原文出处!