Android MMS/SMS 入口

转载http://www.oschina.net/question/54100_36978

MMS - 入口和联系人,想到什么说什么。

在MMS中最重要的两个ui,或者说是用户操作短信的入口,一个是conversationList(短信列表界面) ,另一个就是ComposeMessageActiity(单个对话或者短信),以后简称CMA。

每个CMA 录属于一个Conversation或者不属于任何conversation(无收件人草稿);每个converation由独立的threadId来相互区分

每个CMA有一个WorkingMessage,唯一,表示这个thread(话题)下,用户编辑的短信草稿。

CMA和ConversationList的launch mode都是singleTop,(关于launch mode,请参考 http://androidappdocs.appspot.com/guide/topics/fundamentals.html)

android的launch mode涉及到Task和Activity,简单点说,用户看到的每个界面,基本上都是一个个的Activity,而这些Activity以栈的形式放入在Task中,比如进栈操作由用户的点击进入其他Activity产生,出栈由用户按下back键完成。

一个Task的属性,由他拥有的第一个Activity,也就是栈底的那个activity决定。

一个Activity的launch mode,就是决定他在Task中的表现特点,比如默认的standard模式,就是没有特点,可以在一个Task里随便加,随便加几个instance都成,还能同时在多个Task中共存,就像IE打开网页那种感觉,没有什么限制。

singleTask是整个手机中只能有一个,而且必须出现在这个Task的栈底。

singleInstance表示这个Acitivity独占了整个Task,这个task只有这么一个Activity,最霸道。

singleTop和standard有点类似,区别是,他会去重用Task顶层的他自己的实体,有点绕,像文档上说的,举个例子就明白了。

比如说ABC是3个Activity,mode是standard,在一个栈中的排列是A-B-C,这时候由C再new一个C的intent时,Task变成A-B-C-C;

如果C的mode凑巧是singleTop,那么顶部只能是一个C,也就是在A-B-C这个场景下,再来一个C时,Task变成A-B-C。只是现在顶部的C已经是崭新的C,不是原来那个。

另外,比如launcher按下一个C的图标,这时候,系统会检索所有以C为栈底的Task,如果找到了,就把他起起来,如果没有,就起一个新的。

(I)

CMA的 onSaveInstanceState 函数

引用一段在文档上的说明,

Called to retrieve per-instance state from an activity before being killed

so that the state can be restored in onCreate(Bundle) or onRestoreInstanceState(Bundle) (the Bundle populated by this method will be passed to both).

This method is called before an activity may be killed so that when it comes back some time in the future it can restore its state. For example, if activity B is launched in front of activity A, and at some point activity A is killed to reclaim resources, activity A will have a chance to save the current state of its user interface via this method so that when the user returns to activity A, the state of the user interface can be restored via onCreate(Bundle) or onRestoreInstanceState(Bundle).

Do not confuse this method with activity lifecycle callbacks such as onPause(), which is always called when an activity is being placed in the background or on its way to destruction, or onStop() which is called before destruction. One example of when onPause() and onStop() is called and not this method is when a user navigates back from activity B to activity A: there is no need to call onSaveInstanceState(Bundle) on B because that particular instance will never be restored, so the system avoids calling it. An example when onPause() is called and not onSaveInstanceState(Bundle) is when activity B is launched in front of activity A: the system may avoid calling onSaveInstanceState(Bundle) on activity A if it isn't killed during the lifetime of B since the state of the user interface of A will stay intact.

The default implementation takes care of most of the UI per-instance state for you by calling onSaveInstanceState() on each view in the hierarchy that has an id, and by saving the id of the currently focused view (all of which is restored by the default implementation of onRestoreInstanceState(Bundle)). If you override this method to save additional information not captured by each individual view, you will likely want to call through to the default

implementation, otherwise be prepared to save all of the state of each view yourself.

If called, this method will occur before onStop(). There are no guarantees about whether it will occur before or after onPause().

,大概是说: 这个函数的调用,和onPause()以及onStop()都有区别,但是能保证如果CMA被杀死,他能在onStop()前被调用。

这个函数调用发生的场景发生在类似于,CMA调用了某个Activity: AS, 这个AS运行中CMA不幸要阵亡了,那么,因为用户的界面在AS上,用户可能希望在按下back建或者从home进入之类的场景下,还能重现出CMA当时的状态,于是有了这个函数被调用的必要。

在CMA的onSaveInstanceState()函数中,会保存一份联系人,同时保存workingMessage的subject标题/body正文/uri草稿在数据库中的位置 。

这些信息就够恢复用户离开时的场景了。

(II) initialize

mWorkingMessage = WorkingMessage.createEmpty(this);

initActivityState时,

如果之前CMA在onSaveInstanceState时报存了联系人,则会从旧有联系人里面读取出来,联系人相互之间用“;”分割开 。 workingmessage也由之前保存的变量初始化,这个初始化就基本完成了,返回了。

如果没有保存,则从传入的intent中读取thread_id值,进而得到初始化需要的信息:

如果thread_id>0 ; 那么mConversation从Conversation的cache中,有thread_id取得。

如果thread_id<=0;那么从intent的Date获得thread_id,

再不行,就说明之前没有过这个thread了,可能用户是手写的地址,就从address去产生一个ContactList,从ContactList去Conversation的Cache里匹配得到相应的Conversation

这时候,如果匹配还是没有得到Conversation时,Cache里就会偷偷生成一个Conversation返回回来,适用于你这个状况。

Cache用来保证联系人匹配的所有消息,被放入同一个对话(thread)中。

再还有如果联系人也是空的,那么直接产生一个新的Conversation; 这个Conversation是廉价的,因为他没有thread_id,不被放入cache,在使用ensurethreadid之前,他可以创建无数个,可以随意调用创建出来。

有了conversation之后,就可以initMessageList,查询显示出这个thread下面的所有消息

handleSendIntent() 函数

判断是否由一个intent调用起来的CMA,

特征是intent可以得到Action和mimeType; 比如ACTION_SEND和ACTION_SEND_MULTIPLE; 文件流比如EXTRA_STREAM,字符串EXTRA_TEXT;

遇到这些情况,直接添加附件,其他流程类似于messagelist继续。

handleForwardedMessage() 函数

在不是intent调用的CMA时,检查是不是由转发发起的调用

这些检查归根到底是检查intent的extra中带的关键字,比如 “forwarded_message” , intent.getAction() ,“recipients” , “thread_id”

(III)

CMA的入口只有两个:

onCreate()和 onNewIntent()

这俩个初始化函数的核心是: initialize(Bundle savedInstanceState)

onCreate比较简单,他的内容主要是:

初始化变量 ; 初始化UI ; initialize()

onNewIntent, 只发生在,singleTop的场景,也就是说,已经有一个CMA在Task的顶部(也就是用户的视线内,或者被隐藏到后台了),

之后在从launcher的短信或者从statusbar的短信入口进入时,就会调用到onNewIntent。

在onNewIntent调用时,需要保存原有的CMA的状态,并用新的Intent里的信息,形成新的CMA,从而替代他。

原有的Intent的信息在mConversation和mWorkingMessage里面,

比如mConversation.getThreadId() 得到threadId,如果threadId是0,则说明原来的CMA包含一个草稿,需要保存

比如mWorkingMessage里面,包含最新的收件人信息,需要用mWorkingMessage.syncWorkingRecipients()复制到conversation中,这时候把这份联系人复制到Conversation里,以备保存草稿用

关于新起来的这个Intent和原来的CMA是不是属于同一个conversation的判断;

判断的唯一依据是联系人列表

新来的intent的uri调用uri.getSchemeSpecificPart()函数返回的是一个由“;“分割的电话号码序列,可以拼装成ContactList,是新来的联系人列表。

通过对比,可以知道原来的和信来的两者之间的ContactList是否相同;

如果对比是不同的,那么新的intent将占据CMA,如果对比相同,则没有更换Conversation以及占据这个操作。

‘这个占据包括三步:

1,saveDraft() 函数 ,保存原有conversation的草稿 ,(mRecipientsContainer同步到workingmessage中去。)

2,initialize(null)函数 ,也即(I)里提到的流程,只是传入的初始化参数是null,也就是说在init时,没有在bundle里提供联系人

3,loadMessageContent()函数,查询Conversation下的消息状况,初始化联系人信息,画相关按钮界面

(IV)总结

onCreate()入口:

1 新建短信;bundle和intent都为空初始化

2 在conversation点击一条短信: intent非空,由intent初始化

3 转发:handleForwardedMessage时把intent中的uri中的数据取出来放入mWorkingMessage,有文本以及可能的彩信;同时把list的cursor置空。

4 文件或者文字通过短信发送

5 handleSendIntent处理,如果intent中有extra内容,可以附带Intent.EXTRA_STREAM或者Intent.EXTRA_TEXT;

前者就是一个文件流,可以解析出来多媒体文件,后者是一个文本,

还有一种是Intent.ACTION_SEND_MULTIPLE, 不怎么用,看名字大概知道是怎么回事,不过不懂细节。

你可能感兴趣的:(android)