最近做項目要解析手機中簡訊,擷取簡訊有兩種方式:一種是通過廣播另一種就是通過資料庫,由於項目需要我選擇的是第二種方式,檢索資料庫來擷取簡訊資訊並且進行解析指定內容的簡訊。
在做任何一個新的功能的時候,我們首先查閱一下相關的知識,然後看看有沒有相關的demo,最後建議反過來看Android應用程式層源碼,看看它裡面的一些常量的定義,建議使用高版本的api,你不一定能夠使用它,但是它會給你指出一些思路,就拿簡訊這個功能為例:
既然我們要擷取簡訊的資訊,那就要只要它的各個組成部分,我們進入到android.jar裡面中的android.provider.Telephony,我們可以看到Sms的相關的屬性。如果你調用這裡面的內容的話,會提示Field requires API level 19 (current min is ),但是這並不妨礙我們去模仿他, 我的需要是要檢索收件匣,所以搜尋資料庫的時候,我只需要搜尋收件匣裡面的簡訊,在Telephony中TextBasedSmsColumns介面裡面Message type根據需要我選擇的是MESSAGE_TYPE_INBOX,接下來我們看一下Sms這個介面:
/** * Contains all text-based SMS messages. */ public static final class Sms implements BaseColumns, TextBasedSmsColumns { /** * Not instantiable. * @hide */ private Sms() { } /** * Used to determine the currently configured default SMS package. * @param context context of the requesting application * @return package name for the default SMS package or null */ public static String getDefaultSmsPackage(Context context) { ComponentName component = SmsApplication.getDefaultSmsApplication(context, false); if (component != null) { return component.getPackageName(); } return null; } /** * Return cursor for table query. * @hide */ public static Cursor query(ContentResolver cr, String[] projection) { return cr.query(CONTENT_URI, projection, null, null, DEFAULT_SORT_ORDER); } /** * Return cursor for table query. * @hide */ public static Cursor query(ContentResolver cr, String[] projection, String where, String orderBy) { return cr.query(CONTENT_URI, projection, where, null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy); } /** * The {@code content://} style URL for this table. */ public static final Uri CONTENT_URI = Uri.parse("content://sms"); /** * The default sort order for this table. */ public static final String DEFAULT_SORT_ORDER = "date DESC"; /** * Add an SMS to the given URI. * * @param resolver the content resolver to use * @param uri the URI to add the message to * @param address the address of the sender * @param body the body of the message * @param subject the pseudo-subject of the message * @param date the timestamp for the message * @param read true if the message has been read, false if not * @param deliveryReport true if a delivery report was requested, false if not * @return the URI for the new message * @hide */ public static Uri addMessageToUri(ContentResolver resolver, Uri uri, String address, String body, String subject, Long date, boolean read, boolean deliveryReport) { return addMessageToUri(resolver, uri, address, body, subject, date, read, deliveryReport, -1L); } /** * Add an SMS to the given URI with the specified thread ID. * * @param resolver the content resolver to use * @param uri the URI to add the message to * @param address the address of the sender * @param body the body of the message * @param subject the pseudo-subject of the message * @param date the timestamp for the message * @param read true if the message has been read, false if not * @param deliveryReport true if a delivery report was requested, false if not * @param threadId the thread_id of the message * @return the URI for the new message * @hide */ public static Uri addMessageToUri(ContentResolver resolver, Uri uri, String address, String body, String subject, Long date, boolean read, boolean deliveryReport, long threadId) { ContentValues values = new ContentValues(7); values.put(ADDRESS, address); if (date != null) { values.put(DATE, date); } values.put(READ, read ? Integer.valueOf(1) : Integer.valueOf(0)); values.put(SUBJECT, subject); values.put(BODY, body); if (deliveryReport) { values.put(STATUS, STATUS_PENDING); } if (threadId != -1L) { values.put(THREAD_ID, threadId); } return resolver.insert(uri, values); } /** * Move a message to the given folder. * * @param context the context to use * @param uri the message to move * @param folder the folder to move to * @return true if the operation succeeded * @hide */ public static boolean moveMessageToFolder(Context context, Uri uri, int folder, int error) { if (uri == null) { return false; } boolean markAsUnread = false; boolean markAsRead = false; switch(folder) { case MESSAGE_TYPE_INBOX: case MESSAGE_TYPE_DRAFT: break; case MESSAGE_TYPE_OUTBOX: case MESSAGE_TYPE_SENT: markAsRead = true; break; case MESSAGE_TYPE_FAILED: case MESSAGE_TYPE_QUEUED: markAsUnread = true; break; default: return false; } ContentValues values = new ContentValues(3); values.put(TYPE, folder); if (markAsUnread) { values.put(READ, 0); } else if (markAsRead) { values.put(READ, 1); } values.put(ERROR_CODE, error); return 1 == SqliteWrapper.update(context, context.getContentResolver(), uri, values, null, null); } /** * Returns true iff the folder (message type) identifies an * outgoing message. * @hide */ public static boolean isOutgoingFolder(int messageType) { return (messageType == MESSAGE_TYPE_FAILED) || (messageType == MESSAGE_TYPE_OUTBOX) || (messageType == MESSAGE_TYPE_SENT) || (messageType == MESSAGE_TYPE_QUEUED); }
裡面給出了查詢功能和添加功能,由於我只需要查詢功能,但是裡面public static final Uri CONTENT_URI = Uri.parse("content://sms");說明查詢的是所有的簡訊,而我只要查詢收件匣裡面的,所以上面這就話改成public static final Uri CONTENT_URI = Uri.parse("content://sms/inbox");把Sms和TextBasedSmsColumns介面的內容拷貝出來分別放在兩個介面裡面命名自己設定,而Sms裡面會有一些報錯,有些我們引用了系統不開放的方法,把不相關的代碼進行刪除,整理後的代碼如下:
package com.jwzhangjie.smarttv_client.support.sms1;import android.content.ContentResolver;import android.content.ContentValues;import android.database.Cursor;import android.net.Uri;import android.provider.BaseColumns;public final class Sms implements BaseColumns, TextBasedSmsColumns{ /** * Not instantiable. * @hide */ private Sms() { } /** * Return cursor for table query. * @hide */ public static Cursor query(ContentResolver cr, String[] projection) { return cr.query(CONTENT_URI, projection, null, null, DEFAULT_SORT_ORDER); } /** * Return cursor for table query. * @hide */ public static Cursor query(ContentResolver cr, String[] projection, String where, String orderBy) { return cr.query(CONTENT_URI, projection, where, null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy); } /** * The {@code content://} style URL for this table. */// public static final Uri CONTENT_URI = Uri.parse("content://sms"); public static final Uri CONTENT_URI = Uri.parse("content://sms/inbox"); /** * The default sort order for this table. */ public static final String DEFAULT_SORT_ORDER = "date DESC"; /** * Add an SMS to the given URI. * * @param resolver the content resolver to use * @param uri the URI to add the message to * @param address the address of the sender * @param body the body of the message * @param subject the pseudo-subject of the message * @param date the timestamp for the message * @param read true if the message has been read, false if not * @param deliveryReport true if a delivery report was requested, false if not * @return the URI for the new message * @hide */ public static Uri addMessageToUri(ContentResolver resolver, Uri uri, String address, String body, String subject, Long date, boolean read, boolean deliveryReport) { return addMessageToUri(resolver, uri, address, body, subject, date, read, deliveryReport, -1L); } /** * Add an SMS to the given URI with the specified thread ID. * * @param resolver the content resolver to use * @param uri the URI to add the message to * @param address the address of the sender * @param body the body of the message * @param subject the pseudo-subject of the message * @param date the timestamp for the message * @param read true if the message has been read, false if not * @param deliveryReport true if a delivery report was requested, false if not * @param threadId the thread_id of the message * @return the URI for the new message * @hide */ public static Uri addMessageToUri(ContentResolver resolver, Uri uri, String address, String body, String subject, Long date, boolean read, boolean deliveryReport, long threadId) { ContentValues values = new ContentValues(7); values.put(ADDRESS, address); if (date != null) { values.put(DATE, date); } values.put(READ, read ? Integer.valueOf(1) : Integer.valueOf(0)); values.put(SUBJECT, subject); values.put(BODY, body); if (deliveryReport) { values.put(STATUS, STATUS_PENDING); } if (threadId != -1L) { values.put(THREAD_ID, threadId); } return resolver.insert(uri, values); } /** * Returns true iff the folder (message type) identifies an * outgoing message. * @hide */ public static boolean isOutgoingFolder(int messageType) { return (messageType == MESSAGE_TYPE_FAILED) || (messageType == MESSAGE_TYPE_OUTBOX) || (messageType == MESSAGE_TYPE_SENT) || (messageType == MESSAGE_TYPE_QUEUED);}}
上面的代碼不會有錯誤,查詢功能有了之後,我們還要寫一個儲存查詢出來的內容的類,根據自己的項目編寫,例如:
package com.jwzhangjie.smarttv_client.support.sms1;/** * 資料庫中sms相關的欄位如下: _id 一個自增欄位,從1開始 thread_id 序號,同一發信人的id相同 address 寄件者手機號碼 person 連絡人清單裡的序號,陌生人為null date 發件日期 protocol 協議,分為: 0 SMS_RPOTO, 1 MMS_PROTO read 是否閱讀 0未讀, 1已讀 status 狀態 -1接收,0 complete, 64 pending, 128 failed type ALL = 0; INBOX = 1; SENT = 2; DRAFT = 3; OUTBOX = 4; FAILED = 5; QUEUED = 6; body 簡訊內容 service_center 簡訊服務中心號碼編號 subject 簡訊的主題 reply_path_present TP-Reply-Path locked */import android.os.Parcel;import android.os.Parcelable;/** * 儲存短息資訊 * @author jwzhangjie * */public class SMSInfo implements Parcelable{@Overridepublic int describeContents() {return 0;}/** * 簡訊序號 */private long id;/** * 協議 0:SMS_PROTO 簡訊 1:MMS_PROTO多媒體訊息 */private int protocol;/** * 簡訊內容 */private String smsBody;/** * 發送短息的電話號碼 */private String smsPhoneNum;/** * 傳送簡訊的日期和時間 */private String smsDateTime;/** * 傳送簡訊人 */private String smsSender;/** * 簡訊類型:1是接收到的 2是已發送的 */private int smsType;public SMSInfo(){}private SMSInfo(Parcel source){readFromParcel(source);}public void readFromParcel(Parcel source){id = source.readLong();protocol = source.readInt();smsBody = source.readString();smsPhoneNum = source.readString();smsDateTime = source.readString();smsSender = source.readString();smsType = source.readInt();}@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeLong(id);dest.writeInt(protocol);dest.writeString(smsBody);dest.writeString(smsPhoneNum);dest.writeString(smsDateTime);dest.writeString(smsSender);dest.writeInt(smsType);}public static Creator CREATOR = new Creator() {@Overridepublic SMSInfo createFromParcel(Parcel source) {return new SMSInfo(source);}@Overridepublic SMSInfo[] newArray(int size) {return new SMSInfo[size];}};@Overridepublic String toString() {StringBuilder builder = new StringBuilder();builder.append("id :"+id+" 協議:"+protocol+" 內容:"+smsBody);return builder.toString();}public long getId() {return id;}public void setId(long id) {this.id = id;}public int getProtocol() {return protocol;}public void setProtocol(int protocol) {this.protocol = protocol;}public String getSmsBody() {return smsBody;}public void setSmsBody(String smsBody) {this.smsBody = smsBody;}public String getSmsPhoneNum() {return smsPhoneNum;}public void setSmsPhoneNum(String smsPhoneNum) {this.smsPhoneNum = smsPhoneNum;}public String getSmsDateTime() {return smsDateTime;}public void setSmsDateTime(String smsDateTime) {this.smsDateTime = smsDateTime;}public String getSmsSender() {return smsSender;}public void setSmsSender(String smsSender) {this.smsSender = smsSender;}public int getSmsType() {return smsType;}public void setSmsType(int smsType) {this.smsType = smsType;}}
接下來就是擷取資料庫內容,我們先看一下查詢資料庫的方法:
/** * Return cursor for table query. * @hide */ public static Cursor query(ContentResolver cr, String[] projection, String where, String orderBy) { return cr.query(CONTENT_URI, projection, where, null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy); }
CONTENT_URI:就是我們上面定義的public static final Uri CONTENT_URI = Uri.parse("content://sms/inbox");
projection:這個可以是null,那就是把所有的列都擷取出來,不過不必,你需要什麼就擷取什麼,不然既浪費時間又浪費記憶體
where:查詢條件
orderBy:排序
package com.jwzhangjie.smarttv_client.support.sms1;import java.util.ArrayList;import java.util.List;import android.content.ContentResolver;import android.content.Context;import android.database.Cursor;public class GetSMSInfo {private ContentResolver mResolver;private Context context;private List infos;private static final String[] PROJECTION = new String[]{Sms._ID,//0:作為一個標誌位,記錄最後一個,作為下一次掃描的第一個Sms.TYPE,//1:收還是發Sms.ADDRESS,//2手機號Sms.BODY,//3內容Sms.DATE,//4日期Sms.PROTOCOL//5協議};public static final int COLUMN_INDEX_ID = 0;public static final int COLUMN_INDEX_TYPE = 1; public static final int COLUMN_INDEX_PHONE = 2; public static final int COLUMN_INDEX_BODY = 3; public static final int COLUMN_INDEX_DATE = 4; public static final int COLUMN_INDEX_PROTOCOL = 5;public GetSMSInfo(Context context){this.infos = new ArrayList();this.mResolver = context.getContentResolver();this.context = context;}/** * 擷取簡訊各種資訊 */public List getSmsInfo(){//擷取上一次檢索的最後一個IDlong last_sms_id = UPSharedPreferences.getLong(context, "card", "last_sms_id", 0);Cursor cursor = Sms.query(mResolver, PROJECTION, "_id >" + last_sms_id, "_id");int protocol;String body, date;if (cursor != null) {while (cursor.moveToNext()) {last_sms_id = cursor.getLong(COLUMN_INDEX_ID);body = cursor.getString(COLUMN_INDEX_BODY);date = cursor.getString(COLUMN_INDEX_DATE);protocol = cursor.getInt(COLUMN_INDEX_PROTOCOL);if (protocol == 0) {//收資訊SMSInfo info = new SMSInfo();info.setId(last_sms_id);info.setSmsBody(body);info.setSmsDateTime(date);infos.add(info);}}cursor.close();}//儲存當前最新的IDUPSharedPreferences.setLong(context, "card", "last_sms_id", last_sms_id);return infos;}}讀取得所有合格簡訊都放在List裡面,然後我們最後解析條件一個一個解析,這個涉及一些私人資訊,就不顯示效果了。