Android4.2
1.草稿箱---存储。(ComposeMessageActivity)(type=3)
onKeyDown方法的case KeyEvent.KEYCODE_BACK:调用了exitComposeMessageActivity方法,主要判断离开时是否应该存储界面内的信息,或者提示信息会被舍弃,如果需要存储则把mToastForDraftSave设为true,这个参数决定了在saveDraft方法存储时给toast提示。
在onStop方法中调用了saveDraft(true)方法存储草稿。在saveDraft方法内,判断 如果不应该存储并且联系人编辑框不可见或者里面没联系人,则mWorkingMessage.discard()舍弃信息,discard()舍弃的过程为:把mDiscarded设置为true,接着删除可能存在的与此thread_Id关联的draft。
if (mHasMmsDraft) {
asyncDeleteDraftMmsMessage(mConversation);
}
if (mHasSmsDraft) {
asyncDeleteDraftSmsMessage(mConversation);
}
清除数据库中对应thread_id草稿,彩信的条件为
// If the thread id is < 1, then the thread_id in the pdu will be "" or NULL. We have
// to clear those messages as well as ones with a valid thread id.
final String where = Mms.THREAD_ID + (threadId > 0 ? " = " + threadId : " IS NULL");
asyncDelete(Mms.Draft.CONTENT_URI, where, null);
短信则清除type=3的数据
if (threadId > 0) {
asyncDelete(ContentUris.withAppendedId(Sms.Conversations.CONTENT_URI, threadId),
SMS_DRAFT_WHERE, null);
}
清除mWorkingMessage内Conversation对象的thread_Id参数。
如果没舍弃信息,则调用mWorkingMessage.saveDraft(isStopping)存储。
public void saveDraft(final boolean isStopping) {
// If we have discarded the message, just bail out.
if (mDiscarded) {
LogTag.warn("saveDraft mDiscarded: true mConversation: " + mConversation +
" skipping saving draft and bailing");
return;
}
// Make sure setConversation was called.
if (mConversation == null) {
throw new IllegalStateException("saveDraft() called with no conversation");
}
if (LogTag.VERBOSE || Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
LogTag.debug("saveDraft for mConversation " + mConversation);
}
// Get ready to write to disk. But don't notify message status when saving draft
prepareForSave(false /* notify */);
if (requiresMms()) {
if (hasMmsContentToSave()) {
asyncUpdateDraftMmsMessage(mConversation, isStopping);
mHasMmsDraft = true;
}
} else {
String content = mText.toString();
// bug 2169583: don't bother creating a thread id only to delete the thread
// because the content is empty. When we delete the thread in updateDraftSmsMessage,
// we didn't nullify conv.mThreadId, causing a temperary situation where conv
// is holding onto a thread id that isn't in the database. If a new message arrives
// and takes that thread id (because it's the next thread id to be assigned), the
// new message will be merged with the draft message thread, causing confusion!
if (!TextUtils.isEmpty(content)) {
asyncUpdateDraftSmsMessage(mConversation, content, isStopping);
mHasSmsDraft = true;
} else {
// When there's no associated text message, we have to handle the case where there
// might have been a previous mms draft for this message. This can happen when a
// user turns an mms back into a sms, such as creating an mms draft with a picture,
// then removing the picture.
asyncDeleteDraftMmsMessage(mConversation);
mMessageUri = null;
}
}
}
存短信草稿后把对应thread_id的彩信删掉(如果存在),存彩信时也一样。
存短信的具体代码如下(按理说不应该存在thread_id=0的数据):(测试mMessageUri == null,新建下方法不被调用,另一种情况应该是打开草稿的情况)
private void updateDraftSmsMessage(final Conversation conv, String contents) {
final long threadId = conv.getThreadId();
if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
LogTag.debug("updateDraftSmsMessage tid=%d, contents=\"%s\"", threadId, contents);
}
// If we don't have a valid thread, there's nothing to do.
if (threadId <= 0) {
return;
}
ContentValues values = new ContentValues(3);
values.put(Sms.THREAD_ID, threadId);
values.put(Sms.BODY, contents);
values.put(Sms.TYPE, Sms.MESSAGE_TYPE_DRAFT);
SqliteWrapper.insert(mActivity, mContentResolver, Sms.CONTENT_URI, values);
asyncDeleteDraftMmsMessage(conv);
mMessageUri = null;
}
存彩信使用framework里面类存储的,先获取uri,再存储。
private static void updateDraftMmsMessage(Uri uri, PduPersister persister,
SlideshowModel slideshow, SendReq sendReq, HashMap preOpenedFiles) {
if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
LogTag.debug("updateDraftMmsMessage uri=%s", uri);
}
if (uri == null) {
Log.e(TAG, "updateDraftMmsMessage null uri");
return;
}
persister.updateHeaders(uri, sendReq);
final PduBody pb = slideshow.toPduBody();
try {
persister.updateParts(uri, pb, preOpenedFiles);
} catch (MmsException e) {
Log.e(TAG, "updateDraftMmsMessage: cannot update message " + uri);
}
slideshow.sync(pb);
}
2、加载(WorkingMessage、DraftCache、Conversation)
在Activity的onStart方法及ListView的size变化时都会调用loadMessagesAndDraft()方法,这个方法会调用loadMessageContent()方法加载数据库里的短彩信。接着调用loadDraft()方法加载草稿箱。通过WorkingMessage.loadDraft方法查询草稿箱。在WorkingMessage的loadDraft方法里用异步线程查询,调用readDraftSmsMessage(Conversation conv)方法,此方法里做了限制,如果要查询的thread_id<=0或者!conv.hasDraft()则不查询返回
long thread_id = conv.getThreadId();
if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
Log.d(TAG, "readDraftSmsMessage conv: " + conv);
}
// If it's an invalid thread or we know there's no draft, don't bother.
if (thread_id <= 0 || !conv.hasDraft()) {
return "";
}
Uri thread_uri = ContentUris.withAppendedId(Sms.Conversations.CONTENT_URI, thread_id);
String body = "";
Cursor c = SqliteWrapper.query(mActivity, mContentResolver,
thread_uri, SMS_BODY_PROJECTION, SMS_DRAFT_WHERE, null, null);
如果查询到了短信,则删掉数据库的这条草稿记录再返回结果,彩信不删除
Cursor c = SqliteWrapper.query(mActivity, mContentResolver,
thread_uri, SMS_BODY_PROJECTION, SMS_DRAFT_WHERE, null, null);
boolean haveDraft = false;
if (c != null) {
try {
if (c.moveToFirst()) {
body = c.getString(SMS_BODY_INDEX);
haveDraft = true;
}
} finally {
c.close();
}
}
// We found a draft, and if there are no messages in the conversation,
// that means we deleted the thread, too. Must reset the thread id
// so we'll eventually create a new thread.
if (haveDraft && conv.getMessageCount() == 0) {
asyncDeleteDraftSmsMessage(conv);
// Clean out drafts for this thread -- if the recipient set changes,
// we will lose track of the original draft and be unable to delete
// it later. The message will be re-saved if necessary upon exit of
// the activity.
clearConversation(conv, true);
}
if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
LogTag.debug("readDraftSmsMessage haveDraft: ", !TextUtils.isEmpty(body));
}
return body;
总结:很明显,如果conv.hasDraft()为false是得不到草稿信息的,Conversation的hasDraft方法实际调用DraftCache.getInstance().hasDraft(mThreadId),而DraftCache的hasDraft则是判断这个thread_id有没有在里面的HashSet中记录。
疑问:DraftCache什么时候得到草稿箱的thread_id?
在程序初始化时,在MmsApp的onCreate中调用了DraftCache.init()方法,此方法中构造了单例DraftCache对象,构造时调用refresh()方法,再里面调用rebuildCache()方法获取数据库内所有有草稿的thread_id给静态Hashset对象mDraftSet。在保存、
注意:当离开短信界面时onStop方法里调用的saveDraft(boolean isStopping)方法里
if (mWorkingMessage.isDiscarded()) {
return;
}
if (!mWaitingForSubActivity &&
!mWorkingMessage.isWorthSaving() &&
(!isRecipientsEditorVisible() || recipientCount() == 0)) {
if (LogTag.VERBOSE || Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
log("not worth saving, discard WorkingMessage and bail");
}
mWorkingMessage.discard();
return;
}
mWorkingMessage.saveDraft(isStopping);
也就是说不存储则舍弃,而在discard()方法里调用 clearConversation(mConversation, true), clearConversation方法里调用conv.setDraftState(false);一直调用到DraftCache中
synchronized (mDraftSetLock) {
if (hasDraft) {
changed = mDraftSet.add(threadId);
} else {
changed = mDraftSet.remove(threadId);
}
}