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;}@Overridepublic 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);}@Overridepublic 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)以后讀取短信權限必須在運行時申請。
感謝大家的支持,如有錯誤請指正,如需轉載請標明原文出處!
總結
- 上一篇: 在Unity中实现基于粒子的水模拟(二:
- 下一篇: Win11亮度无法调节解决方法(在卸载驱