Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android_通過ContentObserver監聽短信數據變化

Android_通過ContentObserver監聽短信數據變化

編輯:關於Android編程

1.簡介

在小米等一些機型,無法接收系統發出的短信廣播。只能通過觀察者ContentObserver,去監聽短信數據的變化

2.SMS數據介紹

content://sms/inbox 收件箱
content://sms/sent 已發送
content://sms/draft 草稿
content://sms/outbox 發件箱
content://sms/failed 發送失敗
content://sms/queued 待發送列表

在模擬器上Outbox沒有查詢到數據,在模擬器上找了老半天也沒找到發件箱,很郁悶。
數據庫中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

檢索數據方法很簡單:
Uri uri = Uri.parse("content://sms/inbox");
Cursor cur = this.managedQuery(uri, null, null, null, null);
if (cur.moveToFirst()) {
do{
for(int j = 0; j < cur.getColumnCount(); j++){
info = "name:" + cur.getColumnName(j) + "=" + cur.getString(j);
Log.i("====>", info);
}
}while(cur.moveToNext());
}

managedQuery最終也要將參數轉換為SQL語句向SQLite發送消息,因此參數跟SQL語句很類似,所以可以在查詢字段中加入SQL函數,
比如new String[] projection = new String[]{"count(*) as count"}等等。
managedQuery中的參數依次為uri,
查詢字段 查詢字段數組,也可以將所有需要查詢的字段放入一個字符內
比如new projection[]{"_id", "thread_id"}和new projection[]{"_id,thread_id"}是一致的。
跟SQL一樣,字段名不區分大小寫
條件 不帶Where的SQL 條件字符,如果有參數則用?替代,比如"_id=? And thread_id = ? Or type = '1'"
條件中的參數 參數字符數組,跟上述的條件一一對應
排序 不帶Order by排序字符串,比如_id desc, type
如果參數為null,SQL中查詢字段為“*”,相關的條件為空白

還可以用getContentResolver()獲得一個ContentResolver,
getContentResolver().query()同樣返回一個Cursor對象,參數跟managedQuery一致。
不過用ContentResolver對象去更新、刪除和插入一條數據時報SecurityException。看來沒有權限,在Manifest.xml中加入權限:

然後刪除短信:
this.getContentResolver().delete(Uri.parse("content://sms"), "_id=?", new String[]{"3"});
刪除成功。
Url中content://sms 替換成content://sms/ 也成功,但是其它url時程序報錯,比如content://sms/inbox

看了一下android的源代碼,sms支持的協議有:
sURLMatcher.addURI("sms", null, SMS_ALL);
sURLMatcher.addURI("sms", "#", SMS_ALL_ID);
sURLMatcher.addURI("sms", "inbox", SMS_INBOX);
sURLMatcher.addURI("sms", "inbox/#", SMS_INBOX_ID);
sURLMatcher.addURI("sms", "sent", SMS_SENT);
sURLMatcher.addURI("sms", "sent/#", SMS_SENT_ID);
sURLMatcher.addURI("sms", "draft", SMS_DRAFT);
sURLMatcher.addURI("sms", "draft/#", SMS_DRAFT_ID);
sURLMatcher.addURI("sms", "outbox", SMS_OUTBOX);
sURLMatcher.addURI("sms", "outbox/#", SMS_OUTBOX_ID);
sURLMatcher.addURI("sms", "undelivered", SMS_UNDELIVERED);
sURLMatcher.addURI("sms", "failed", SMS_FAILED);
sURLMatcher.addURI("sms", "failed/#", SMS_FAILED_ID);
sURLMatcher.addURI("sms", "queued", SMS_QUEUED);
sURLMatcher.addURI("sms", "conversations", SMS_CONVERSATIONS);
sURLMatcher.addURI("sms", "conversations/*", SMS_CONVERSATIONS_ID);
sURLMatcher.addURI("sms", "raw", SMS_RAW_MESSAGE);
sURLMatcher.addURI("sms", "attachments", SMS_ATTACHMENT);
sURLMatcher.addURI("sms", "attachments/#", SMS_ATTACHMENT_ID);
sURLMatcher.addURI("sms", "threadID", SMS_NEW_THREAD_ID);
sURLMatcher.addURI("sms", "threadID/*", SMS_QUERY_THREAD_ID);
sURLMatcher.addURI("sms", "status/#", SMS_STATUS_ID);
sURLMatcher.addURI("sms", "sr_pending", SMS_STATUS_PENDING);
sURLMatcher.addURI("sms", "sim", SMS_ALL_SIM);
sURLMatcher.addURI("sms", "sim/#", SMS_SIM);

其中,delete方法中支持的協議為:
SMS_ALL 根據參數中的條件刪除sms表數據
SMS_ALL_ID 根據_id刪除sms表數據
SMS_CONVERSATIONS_ID 根據thread_id刪除sms表數據,可以帶其它條件
SMS_RAW_MESSAGE 根據參數中的條件刪除 raw表
SMS_STATUS_PENDING 根據參數中的條件刪除 sr_pending表
SMS_SIM 從Sim卡上刪除數據

試一下SMS_CONVERSATIONS_ID:"content://sms/conversations/3 ",刪除thread_id="3", _id="5"的數據
在eclipse中的Emulator Control中,以13800給模擬器發送三條數據,然後以13900發送一條
this.getContentResolver().delete(Uri.parse("content://sms/conversations/3"), "_id=?", new String[]{"5"});
成功刪除一條數據。
在數據庫中每個發送者的thread_id雖然一樣,但不是固定的,如果把一個發送者的全部數據刪除掉,
然後換一個新號碼發送短信時,thread_id是以數據庫中最大的id+1賦值的。

update支持的協議有很多:
SMS_RAW_MESSAGE
SMS_STATUS_PENDING
SMS_ALL
SMS_FAILED
SMS_QUEUED
SMS_INBOX
SMS_SENT
SMS_DRAFT
SMS_OUTBOX
SMS_CONVERSATIONS
SMS_ALL_ID
SMS_INBOX_ID
SMS_FAILED_ID
SMS_SENT_ID
SMS_DRAFT_ID
SMS_OUTBOX_ID
SMS_CONVERSATIONS_ID
SMS_STATUS_ID

以SMS_INBOX_ID測試一下:
ContentValues cv = new ContentValues();
cv.put("thread_id", "2");
cv.put("address", "00000");
cv.put("person", "11");
cv.put("date", "11111111");
this.getContentResolver().update(Uri.parse("content://sms/inbox/4"), cv, null, null);
太強了,連thread_id都可以修改。

insert支持的協議:
SMS_ALL
SMS_INBOX
SMS_FAILED
SMS_QUEUED
SMS_SENT
SMS_DRAFT
SMS_OUTBOX
SMS_RAW_MESSAGE
SMS_STATUS_PENDING
SMS_ATTACHMENT
SMS_NEW_THREAD_ID

向sms表插入數據時,type是根據協議來自動設置,
如果傳入的數據中沒有設置date時,自動設置為當前系統時間;非SMS_INBOX協議時,read標志設置為1
SMS_INBOX協議時,系統會自動查詢並設置PERSON
threadId為null或者0時,系統也會自動設置

一直為造不了"發送失敗"的郵件而發愁,現在來做一個:
content://sms/failed
ContentValues cv = new ContentValues();
cv.put("_id", "99");
cv.put("thread_id", "0");
cv.put("address", "9999");
cv.put("person", "888");
cv.put("date", "9999");
cv.put("protocol", "0");
cv.put("read", "1");
cv.put("status", "-1");
//cv.put("type", "0");
cv.put("body", "@@@@@@@@@");
this.getContentResolver().insert(Uri.parse("content://sms/failed"), cv);
type被設置成了5,thread_id設置為1

看看能不能再挖掘一下sms的功能。先來做一個錯誤的查詢:
getContentResolver().query( Uri.parse("content://sms/") , new String[]{"a"}, "b", null, null);
log輸出錯誤的SQL語句:
SELECT a FROM sms WHERE (b) ORDER BY date DESC
query方法中沒有Group by,如果想對短信做統計,對Cursor進行遍歷再統計也太慢了。
在SQL語言中group by在Where後面,那就在條件參數中想想辦法:
Android組織SQL語句時將條件兩端加(),那就拼一個group by出來吧:
getContentResolver().query( Uri.parse("content://sms/") , new String[]{"count(*) as count, thread_id"}, "1=1) group by (thread_id", null, null);
那麼輸出的SQL= SELECT count(*) as count, thread_id FROM sms WHERE ( 1=1) group by (thread_id ) ORDER BY date DESC
如果想查詢URI沒有對應的表怎麼辦呢,比如想知道 mmssms.db數據庫中有哪些表,
查詢的表是URI定的,再在條件參數中拼湊肯定是不行。
那我們把目光往前移,看看在字段參數中能不能湊出來。
要查詢其它表,關鍵要去掉系統固定添加的FROM sms,
用用SQL中的注釋吧,
getContentResolver().query(Uri.parse("content://sms/"), new String[]{" * from sqlite_master WHERE type = 'table' -- "}, null, null, null);
那麼輸出的SQL=SELECT * from sqlite_master WHERE type = 'table' -- FROM sms ORDER BY date DESC 居然能夠運行。
得寸進尺,再進一步,如果加入“;”也能運行的話,哈哈,那麼建表、刪除表、更新表也能為所欲為咯。
getContentResolver().query(Uri.parse("content://sms/"), new String[]{" * from sms;select * from thrreads;-- "}, null, null, null);

3.實例

public class SMSContentObserver extends ContentObserver {
	private Context mContext;
	String[] projection = new String[] { "address", "body", "date", "type", "read" };

	public SMSContentObserver(Context context, Handler handler) {
		super(handler);
		mContext = context;
	}

	@Override
	public void onChange(boolean selfChange) {
		Uri uri = Uri.parse("content://sms/inbox");
		Cursor c = mContext.getContentResolver().query(uri, null, null, null, "date desc");
		if (c != null) {
			while (c.moveToNext()) {
				SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
				Date d = new Date(c.getLong(c.getColumnIndex("date")));
				String date = dateFormat.format(d);
				StringBuilder sb = new StringBuilder();
				sb.append("發件人手機號碼: " + c.getString(c.getColumnIndex("address")))
						.append("信息內容: " + c.getString(c.getColumnIndex("body")))
						.append(" 是否查看: " + c.getInt(c.getColumnIndex("read")))
						.append(" 類型: " + c.getInt(c.getColumnIndex("type"))).append(date);
				Log.i("xxx", sb.toString());
			}
			c.close();
		}
	}
}

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved