Android SMS
0 基本原理
SMS: 短信 Short Message Service
Android将短信和彩信都存在 mmssms.db 中, 存放路径 : /data/data/com.android.providers.telephony/databases/mmssms.db , 真机上需要 root 后才能访问该文件。
1 表结构
Android通过 MmsSmsDatabaseHelper.java 来管理SMS 和 MMS,通过查看该类的代码,可以了解到具体的数据库表结构。
短信处理主要使用到表 sms ,编号为55的数据库版本创建sms表的语句如下(汉字注释是我增加的针对字段的说明,并非原代码中的内容):
static final int DATABASE_VERSION = 55;
db.execSQL("CREATE TABLE sms (" +
"_id INTEGER PRIMARY KEY," +
"thread_id INTEGER," + // 会话的序号,同一发信人的id相同
"address TEXT," + // 发件人手机号码
"person INTEGER," + // 联系人列表里的序号,0表示陌生人
"date INTEGER," + // 发件日期
"date_sent INTEGER DEFAULT 0," +
"protocol INTEGER," + // 协议,分为: 0 SMS_RPOTO, 1 MMS_PROTO
"read INTEGER DEFAULT 0," + // 是否阅读 0未读, 1已读
"status INTEGER DEFAULT -1," + // a TP-Status value
// or -1 if it
// status hasn't
// been received
// 状态 -1接收,0 complete, 64 pending, 128 failed
"type INTEGER," + // 取值:
// ALL = 0;
// INBOX = 1;
// SENT = 2;
// DRAFT = 3;
// OUTBOX = 4;
// FAILED = 5;
// QUEUED = 6;
"reply_path_present INTEGER," +
"subject TEXT," + // 短信的主题
"body TEXT," + // 短信内容
"service_center TEXT," + // 短信中心号码
"locked INTEGER DEFAULT 0," +
"error_code INTEGER DEFAULT 0," +
"seen INTEGER DEFAULT 0" +
");");
注意: 数据库中的 INTEGER 对应到Java的 Long 类型。
2 相关 URI
我们不能直接读取 mmssms.db ,只能通过系统提供的 Content Provider来访问。
短信相关的URI有
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", "icc", SMS_ALL_ICC);
sURLMatcher.addURI("sms", "icc/#", SMS_ICC);
//we keep these for not breaking old applications
sURLMatcher.addURI("sms", "sim", SMS_ALL_ICC);
sURLMatcher.addURI("sms", "sim/#", SMS_ICC);
最新的URI可以参见: SmsProvider.java
3 读取短信
3.1 获取短信列表
通常意义上的短信列表是指会话列表,就是同一联系人的短信记录只显示最近一条的那种显示方式。
通过URI地址 content://sms/conversations 可以获取短信的会话列表。但Android系统自带的查询结果只有三个字段。如:
- thread_id 会话编号
- msg_count 该会话中的短信条数
- snippet 该会话中最后一条短信的内容
一般而言,我们还需要在会话列表中显示联系人信息(姓名、头像、电话号码等) 和 最后一次收/发短信的时间。所以还需要对系统的返回结果进行扩充。
关键代码如下:
String[] projection = new String[] { "thread_id as _id", "thread_id", "msg_count", "snippet",
"sms.address as address", "sms.date as date" };
Uri uri = Uri.parse("content://sms/conversations");
Cursor cursor = managedQuery(uri, projection, null, null, "sms.date desc");
- “thread_id as _id” : 为了让查询结果可以传送给 CursorAdapter
- “sms.address as address” : 查询联系人号码
- “sms.date as date” : 查询最后一条短信的日期
- “sms.date desc” : 会话降序排序
3.2 获取短信的联系人
虽然sms表中有person字段,但如果是先接收到短信再将陌生人添加到联系人列表,则person仍然是为0。所以不能依赖 person 字段,只能通过 address 去查询联系人。
声明: 本文采用 CC BY-NC-SA 3.0 协议进行授权,转载请注明出处。