Messaging
介绍
一款系统短信应用
项目结构
app:主程序
chips:系统扩展控件
photoviewer:图片预览器
项目说明
数据加载(同步mmssms.db数据)
-
同步工作从 BugleApplication 创建时发起;
依次执行 DataModel.immediateSync –> SyncManager.immediateSync –> SyncMessagesAction.sync 在这里创建 SyncMessagesAction 对象; -
然后将该SyncMessagesAction对象交由 DataModel 的 ActionService 对象来执行任务;
-
ActionService 通过 ActionServiceImpl 来启动一个 IntentService;
-
在 ActionServiceImpl 里执行 SyncMessagesAction 的 executeAction 方法;
-
在SyncMessagesAction 的 executeAction 方法中判断如果需要执行同步操作,调用 requestBackgroundWork 方法将自己加入 mBackgroundAction队列;
-
执行 Action 的sendBackgroundActions 方法来启动后台任务,这时会传入 DataModel 的 BackgroundWorker 对象;
-
BackgroundWorker 通过 BackgroundWorkerService 来启动一个 InentService 来执行任务;
-
在 BackgroundWorkerService 里调用SyncMessagesAction 的 doBackgroundWork 方法执行后台操作,并将结果用Bundle返回;
-
调用 ActionServiceImpl 的 handleResponseFromBackgroundWorker 或 handleFailureFromBackgroundWorker 方法,启动一个 IntentService 来处理成功或失败的响应。
在 ActionServiceImpl 里执行 SyncMessagesAction 的 processBackgroundWorkResponse 或 processBackgroundWorkFailure 来处理响应。
数据库说明(bugle_db同步mmssms.db的数据)
数据库中数据表:conversations,messages,parts,participants,conversation_participants
视图:draft_parts_view ,coversation_image_parts_view ,conversation_list_view
数据表
数据表 conversation_participants
字段 | 数据类型 | 说明 |
---|---|---|
conversation_id | int | 会话人属于那一场会话 |
participant_id | int | 会话人id |
数据表 conversations
字段 | 数据类型 | 说明 |
---|---|---|
_id | int | 主键ID |
sms_thread_id | int | SMS/MMS Thread ID from the system provider |
name | String | 信息名称 |
latest_message_id | int | 最新消息id |
snippet_text | String | 最新更新的消息的内容(彩信为主题,短信为正文)会话列表中显示的简要消息 |
subject_text | String | 会话列表中显示的简要消息 |
preview_uri | String | 预览Uri |
preview_content_type | String | 预览Uri类型 |
show_draft | int | 是否显示当前草稿 |
draft_snippet_text | String | 最新的草稿简要 |
draft_subject_text | String | 最新的草稿主题 |
draft_preview_uri | String | 草稿预览uri |
draft_preview_content_type | String | 草稿预览uri类型 |
archive_status | archive_status | 会话是否归档 |
sort_timestamp | int | 时间戳用于排序 |
last_read_timestamp | int | 最新阅读短信时间戳 |
icon | String | 会话Icon |
participant_contact_id | int | 参与者联系人ID(如果此对话只有一个参与者)。否则为-1 |
participant_lookup_key | String | 参与者查找键(如果此对话只有一个参与者)。否则为空 |
participant_normalized_destination | String | 参与者的标准化目的地(如果此对话只有一个参与者)。否则为空。 |
current_self_id | String | 自身的id? |
participant_count | int | 参与者人数不包括自己(因此1:1的比例为1或更大的比例为1:1) |
notification_enabled | int | 对话是否启用通知 |
notification_sound_uri | String | 通知铃声 |
notification_vibration | int | 是否启动震动 |
include_email_addr | int | 会话包含Email? |
sms_service_center | String | 短信中心号码 |
IS_ENTERPRISE | int | 是否是企业对话 |
is_top | int | 是否置顶(后期添加字段) |
数据表 messages
字段 | 数据类型 | 说明 |
---|---|---|
id | int | 主键 |
conversation_id | int | 会话id关联conversations中id |
sender_id | int | 发送该消息的会话人id |
sent_timestamp | int | 发送消息时间戳 |
received_timestamp | int | 接收消息时间戳 |
message_protocol | int | 信息类型:短信息、彩信或彩信通知 |
message_status | int | 消息状态 |
seen | int | 是否被用户在通知中看见0和1 |
read | int | 是否被用户已阅读0和1 |
sms_message_uri | String | 来自平台提供程序的消息ID |
sms_priority | int | 信息的优先级 |
sms_message_size | int | 彩信大小 |
mms_subject | String | 彩信主题 |
mms_transaction_id | String | MMS通知的事务id |
mms_content_location | String | 彩信通知的内容位置 |
mms_expiry | int | 过期时间 |
raw_status | int | 详细的状态比如那条短信是发送成功还是失败 |
self_id | int | 代表处理此消息的sim卡的参与者 |
retry_start_timestamp | int | 开始重试的时间。用于计算重试窗口,当重试发送/下载邮件。 |
collected | int | 收藏 |
recycled | int | 回收 |
数据表 parts
字段 | 数据类型 | 说明 |
---|---|---|
_id | INT | 主键自增 |
message_id | INT | 附件属于那条消息 |
conversation_id | INT | 附件属于那次会话 |
text | String | 附件文字 |
uri | String | 附件内容uri |
content_type | String | 附件内容类型 |
width | INT | 此附件的缓存宽度(用于加载时的布局) |
height | INT | 此附件的缓存高度(用于加载时的布局) |
timestamp | INT | 同message表中时间戳 |
数据表 participants
字段 | 数据类型 | 说明 |
---|---|---|
_id | INT | 主键 |
sub_id | INT | 参与者关联的sim卡的订阅id。在L中引入。对于早期版本,将始终使用默认的-1。对于多sim卡设备(或sim卡已更改的情况)单个设备,可能有几个不同的sub_id值 |
sim_slot_id | INT | 此自参与者的活动SIM(插入设备中)的插槽。如果自参与者与任何活动SIM不对应,这将是链接android.telephony.SubscriptionManager.对于所有非自参与者,该列将被忽略 |
normalized_destination | String | 如果可能,电话号码将以标准E164格式存储。这是一个独特的对于给定参与者,我们不能用同一个电话号码处理多个参与者,因为我们不知道消息来自哪一个。这也可以是电子邮件地址,在这种情况下,它与显示的地址相同 |
send_destination | String | 发送短信的号码 |
display_destination | String | 添加行时,根据设备的区域设置对电话号码进行用户友好的格式设置 |
full_name | String | 全名 |
first_name | String | 姓 |
profile_photo_uri | String | 头像 |
contact_id | INT | 联系人id |
lookup_key | String | 包含有关如何在联系人查找中查找联系人信息的提示的字符串 |
blocked | INT | 是否该用户被拦截 |
subscription_color | INT | 订阅的颜色? |
subscription_name | String | 订阅的名字? |
contact_destination | String | 存储在此参与者的联系人中的确切目的地 |
视图
conversation_list_view
查询对话列表
SELECT conversations._id AS _id,
conversations.name AS name,
conversations.current_self_id AS current_self_id,
conversations.archive_status AS archive_status,
messages.read AS read,
conversations.icon AS icon,
conversations.participant_contact_id AS participant_contact_id,
conversations.participant_lookup_key AS participant_lookup_key,
conversations.participant_normalized_destination AS participant_normalized_destination,
conversations.sort_timestamp AS sort_timestamp,
conversations.show_draft AS show_draft,
conversations.draft_snippet_text AS draft_snippet_text,
conversations.draft_preview_uri AS draft_preview_uri,
conversations.draft_subject_text AS draft_subject_text,
conversations.draft_preview_content_type AS draft_preview_content_type,
conversations.preview_uri AS preview_uri,
conversations.preview_content_type AS preview_content_type,
conversations.participant_count AS participant_count,
conversations.notification_enabled AS notification_enabled,
conversations.notification_sound_uri AS notification_sound_uri,
conversations.notification_vibration AS notification_vibration,
conversations.include_email_addr AS include_email_addr,
messages.message_status AS message_status,
messages.raw_status AS raw_status,
messages._id AS message_id,
participants.first_name AS snippet_sender_first_name,
participants.display_destination AS snippet_sender_display_destination,
conversations.IS_ENTERPRISE AS IS_ENTERPRISE,
conversations.is_top AS is_top,
conversations.snippet_text AS snippet_text,
conversations.subject_text AS subject_text
FROM conversations
LEFT JOIN
messages ON (conversations.latest_message_id = messages._id)
LEFT JOIN
participants ON (messages.sender_id = participants._id)
ORDER BY conversations.sort_timestamp DESC;
coversation_image_parts_view
查询图片列表
SELECT messages.conversation_id AS conversation_id,
parts.uri AS uri,
participants.full_name AS _display_name,
parts.uri AS contentUri,
NULL AS thumbnailUri,
parts.content_type AS contentType,
participants.display_destination AS display_destination,
messages.received_timestamp AS received_timestamp,
messages.message_status AS message_status
FROM messages
LEFT JOIN
parts ON (messages._id = parts.message_id)
LEFT JOIN
participants ON (messages.sender_id = participants._id)
WHERE parts.content_type LIKE image/%
ORDER BY messages.received_timestamp ASC,
parts._id ASC;
draft_parts_view
查询草稿附件
SELECT parts._id AS _id,
parts.message_id AS message_id,
parts.text AS text,
parts.uri AS uri,
parts.content_type AS content_type,
parts.width AS width,
parts.height AS height,
messages.conversation_id AS conversation_id
FROM messages
LEFT JOIN
parts ON (messages._id = parts.message_id)
WHERE messages.message_status = 3;
短信发送流程
ComposeMessageView:由mSendButton点击事件进行发起。
InsertNewMessageAction:插入到数据库
SendMessageAction:数据插入成功后调用,执行发送操作
MmsManager:最终调用SmsManager进行发送
短信接收流程
SmsDeliverReceiver:发送短信收到的广播
ReceiveSmsMessageAction:接收短信动作逻辑处理比如插入数据库
BugleNotifications:通知处理,显示通知
数据缓存
其中包含缓存管理(MemoryCacheManager)和媒体资源管理(MediaResourceManager)两部分。
缓存管理使用的是android.util.LRUCache,是最近最少使用(Latest recently used)缓存算法的实现
主要类说明
主页:ConversationListActivity,ConversationListFragment,ConversationListAdapter,ConversationListData
信息列表:ConversationActivity,ConversationFragment,ConversationMessageAdapter,ConversationData
新增对话:ConversationActivity
设置页面:ApplicationSettingsFragment
高级设置:PerSubscriptionSettingsActivity,ContactPickerFragment(选择联系人)
图片预览:BuglePhotoViewActivity
名片预览:VCardDetailActivity,VCardDetailFragment
参与者名单和选项:PeopleAndOptionsActivity,PeopleAndOptionsFragment
附件选择:AttachmentChooserActivity,AttachmentChooserFragment
其他
libframesequence.so(gif图片显示库)编译,依赖于libgiflib.so
giftranscode.so(gif图片转码库)编译,依赖于libgiflib.so
编译命令:
ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Android.mk NDK_APPLICATION_MK=./Application.mk
编码——>ENCODING_7BIT
-
/** The maximum number of payload septets per message */ public static final int MAX_USER_DATA_SEPTETS = 160; //单条信息160个字节(ASCII码对应的纯英文字符) /** * The maximum number of payload septets per message if a user data header * is present. This assumes the header only contains the * CONCATENATED_8_BIT_REFERENCE element. */ public static final int MAX_USER_DATA_SEPTETS_WITH_HEADER = 153;//对于ASCII码对应的纯英文字符,长短信中每条短信的长度为153字节,7字节标记分页信息等协议
对于纯英文,1字符占用1字节 所以相当于第一条160个字符 第二条160-7-7=146个字符(第一条信息的协议信息也占用了第二条的长度)
第三条及以上,160-7=153个字符。
编码——>ENCODING_16BIT (处理包含中文等非ASSICC码字符的短信)
-
/** The maximum number of payload bytes per message */ public static final int MAX_USER_DATA_BYTES = 140;//单条信息140个字节(包含中文等非ASCII码字符的信息) /** * The maximum number of payload bytes per message if a user data header * is present. This assumes the header only contains the * CONCATENATED_8_BIT_REFERENCE element. */ public static final int MAX_USER_DATA_BYTES_WITH_HEADER = 134;//包含中文等非ASCII码字符的信息,长短信每条最大长度为140-6=134字节,6个字节用于标记分页信息
对于包含中文字符的信息,1字符占用2字节,140/2=70, 134/2=67, 6/2=3 所以相当于第一条70个字符 第二条70-3-3=64字符(第一条信息的分页信息也占用了第二条的长度) 第三条及以上70-3=67字符。