Android Mms之:深入MMS支持
composing and editing
mms在android mms应用里面的具体实现形式,或数据结构是slideshowmodel,它是一个每个节点为slidemodel的arraylist,slidemodel是一个model的list,也就是它可以接收任何model的子类,audio,video,image和text都可以放到slidemodel上面。slidemodel主要用于管理其上面的各个媒体,比如它们的布局,它们的播放控制,而slideshowmodel主要用于管理所有的附件,比如把所有的附件转化成为android的mms协议的数据类型pdu,以及从pdu转化成为slideshowmodel。
pdu是实现了mms协议的标准格式,它可以直接的发送给mmsc,从mmsc取回来的也是一个pdu格式的数据。应用层mms不需要关心pdu的具体实现方式,android中有一个内部的包com.google.android.mms.*下面的类都是专门用于处理android平台上的mms。里面提供了工作可以把应用层的数据,比如媒体文件等,进行包装成pdu,再把pdu分解成为媒体文件。pdu的数据结构包括pdubody,这个是用于存放多媒体文件的地方,其里面是pdupart的集合,每个pdupart代表一个文件。pdupersister用于操作这些数据结构,包括写入数据库,从数据库中读取等。
slideshowmodel或俗称幻灯片是应用层的mms的实现形式,或者它是应用层mms用来创建,编辑,显示和管理多媒体的一个数据结构。创建和发送mms的时候,就是创建一个slideshowmodel,构建mediamodel,textmodel等加入到slideshowmodel中。在发送时,slideshowmodel会把其内的媒体文件取出来,转化为pdupart放入pdubody中。收到信息后从pdubody中取出pdupart,还原成媒体文件,生成mediamodel,加入到slideshowmodel中,也就是还原为幻灯片。应用拿到幻灯片后可以做显示和播放。
附件类型
关于附件类型,mms应用中所有的mms都有一个幻灯片,其内含有所有的附件文件。但是mms做了一些特殊的处理,对于一个mms信息,它的附件类型分为image, audio, video,和slideshow,这些从添加附件对话框的列表中可以看出,而且展现方式也有所不同。但是实际的实现上面并没有这么多的类型,只有一个slideshowmodel,所有的附件都在里面。它处理的规则是这样的,如果只添加了一个媒体(image, audio和video)时,会把类型设置为相应的媒体类型,而只有在附件对话框中明确选择添加幻灯片时并且添加了多张幻灯片后,附件类型才会是幻灯片。这个附件类型只在给mms添加附件时和发送mms前有效,主要用于在消息列表中如何展示媒体文件,如果是具体媒体类型,就直接显示,否则显示为幻灯片,这个附件类型仅存在于应用中显示媒体所用,并不会在发送出去的pdu中有痕迹。当收到mms后,也是根据转化后的slideshowmodel里面的内容来推测出附件类型,然后再做显示。所以,对于一个mms来说它始终都有一个slideshowmodel,用户所感受到的附件类型仅是附件媒体显示上面的一个处理而已。
创建和编辑mms
与传统手机不同,创建mms并不需要特殊的方式。因为mms应用对mms和sms并不做严格的区分,而是以统一的对话中的一个消息来对待,所以mms与sms的区别也很简单,就看一个消息中只否有附件(workingmessage.hasattachment())。创建mms也十分简单,只需点击composer而的attach菜单添加媒体即可。在列表中选择image, audio和video后就只有一个媒体文件,都会跑到其他的activity去选择文件,然后会返回其uri给composer,composer会调用workingmessage.setattachment()来做具体的添加,用uri创建mediamodel然后加入到slideshowmodel中,并设置类型。另外,如果选择了attach幻灯片,就会直接进入编辑幻灯片的而面,可以添加删除幻灯片页,给幻灯片页加媒体文件,设置布局等,之后composer会把slideshowmodel显示出来,此时的附件类型也是slideshow,这些都是通过workingmessage.load()来完成的。
workingmessage在把媒体加到幻灯片里以后,就会回调一个接口 onattachmentchanged(),composer实现了此接口,这个接口主要用于通知composer附件已发生变化,刷新ui以正确显示附件。composer会创建attachmenteditor来显示附件的内容,因为所有的附件都放在slideshow里面这个slideshow在workingmessage中,可以通过workingmessage.getslideshow()来获取。attachmenteditor会根据slideshow里面的内容来创建不同的view以展示不同的附件,如果slideshow中只有一个video,audio或image,就直接创建videoattachmentview,audioattachmentview或imageattachmentview,而对于幻灯片中页数大于1时就会创建slideshowattachmentview。还有相应的按扭可以用来编辑,替换或删除,对于单个媒体有查看/播放,选择后可以查看原图和播放音频视频,替换可以重新重选择一个附件,删除会移除掉附件;对于slideshow有编辑和删除,编辑会直接进入幻灯片的编辑页面,那里可以一页一页的对每页幻灯片进行详细的编辑,删除会移除掉附件。
编辑完附件后有三种处理方式,一个是发送信息,一个是保存为草稿另一个就是放弃信息。发送信息和保存草稿都会对幻灯片进行打包,转成pdu,并保存到数据库,之后的幻灯片都需要从数据库加载并把pdu解包成为slidehshowmodel。
packaging and unpackaging mms
要发送信息前,或是保存草稿时,都需要把slideshowmodel进行打包生成pdu格式,并保存至数据库。这个称为mms的打包(packaging),是由slideshowmodel.makepdubody()方法来完成,它会把幻灯片里面的内容一个一个的取出来,转成一个pdupart,再 放入pdubody中,以生成pdubody,一个媒体对应一个pdupart,同时还可以设置pdupart的属性以描述媒体的文件,比如contenttype,这是一个用于标识媒体mime类型的字串;filename文件的名字; contentlocation文件的路径。这些信息都用于描述pdupart中数据的元信息(metadata),也就是数据具体是什么,以便让解包的时候对数据进行正确的处理。
之后pdupersister会通过其persist()方法把pdubody存入到数据库中,它会把pdupart中的描述性信息作数据库字段写入,把文件存储在telephonyprovider文件夹下面(/data/data/android.providers.telephony/app_parts),并把存储后的路径作为_data字段写入数据库,这样一条mms的数据就都写入了数据库中。这以后,mms的数据都是从数据库中加载,所以原slideshowmodel中的数据库不再有效,如uri在原slideshowmodel中可能指向一个文件,或是其他数据库,在pdupersister.persist()之后就不再有效了。
当pdupersister.persist()之后,mms的附件就都从数据中加载,pdupersister.load()会从数据库把数据加载成为一个pdubody,slideshowmodel的方法createfrompdubody()就是用于把pdubody转化成为一个slideshowmodel,从pdupart取出媒体信息以得到正确的媒体格式,和相关信息,可以通过uri来获取具体文件(流)。
接收到的mms过程也差不多当notificationtransaction或retrievetransaction用httputils从mmsc获取到mms数据后会用pduparser来解析数据生成pdu,再用pdupersister.persist()把其写入数据库,之后会再从数据库中加载。
smil语言支持
对于每条mms还有一个很重要的数据就是smil语言,smil是同步多媒体集成语言的简称(synchronized multimedia integration language),它与html文档很类似,是w3c(world wide web consortium)组织规定的多媒体操纵标准语言。mms也是用它来管理和播放多媒体。来看一个具体的smil语言实例:
<smil xmlns="http://www.w3.org/2000/smil20/cr/language">
<head>
<layout>
<root-layout width="360" height="615"/>
<region id="image" width="347" height="260" top="14" left="7" fit="meet"/>
<region id="text" width="326" height="320" top="281" left="7" fit="scroll"/>
</layout>
</head>
<body>
<par dur="60s">
<img src="0.jpg" region="image"/>
<text src ="0.txt" region="text"/>
</par>
<par dur="60s">
<text src ="1.txt" region="text"/>
</par>
<par dur="60s">
<text src ="2.txt" region="text"/>
</par>
<par dur="60s">
<text src ="3.txt" region="text"/>
</par>
<par dur="60s">
<text src ="4.txt" region="text"/>
</par>
</body>
</smil>
smil语言播放多媒体时通常是一页页的,与幻为播放十分类似,因为很多smil播放器都会做成幻灯片形式。因为mms用smil来传送多媒体,所以mms终端应用都会以幻灯片的方式来播放mms。这也就是为什么mms应用中会出来slideshowmodel的原因。幻灯片方式显示彩信是一种常用的方法,即使某些终端应用没有用幻灯片放映的方式显示彩信,但是对于运营商或彩信平台发出来彩信都有页码标识,另外其他的一些手机,比如非智能手机查看彩信的方式也是以幻灯片一页一页的放映。
它主要记载着用于幻灯片的布局信息。这个smil语言就是用于幻灯片布局的,也就是说smil会像html文档布局网页那样来说明如何布局幻灯片,它有这些tag:head, layout, body, par,head是头信息,里面有tag layout用来说明这个幻灯片是如何布局的,具体的它用一些子tag如root-layout, region等来说明幻灯片中的每一个元素如image或text如何布局。tag body中列出了幻灯片的所有媒体元素和详细内容,比如image, audio, text等,每一par是一页,它的子tag说明这一页有哪些内容,当然smil语言还有很多内容可以参考wikipedia上的讲解。
当打包幻灯片时,也就是把slideshowmodel转化为pdu时,会根据slideshowmodel的内容生成一个smil语言,通过smilhelper.getdocument()来生成smil文档,把其加入到pdubody中并作为第一个pdupart,它的contenttype(mime)是application/smil,它的内容就是smil文档。需要注意的是smil文档总是会在pdubody的第一个part,并且它直接把文档内容写到pdupart中,而不是以文件的形式存在。
当解包的时候,会先取出smil文档,对其进行解析,生成幻灯片。
因为smil是一个标准的文档,所以w3c有其相应的规范,也有相应的库来解析和生成。在mms应用中可以看到这样的二个package: org.w3c.dom.*和com.android.mms.dom.*;其中org.w3c.dom是smil语言的一些标准库,而com.android.mms.dom.*;是对org.w3c.dom一些标准接口的实现,或者说是为了mms应用而做的一些适配。那么在com.android.mms.model.*里面的一些类也是根据smil标准而写的,比如smilhelper就是专门用于解析smil文档和生成smil文档,当然它会用到前面提到的二个package里面的东西。还有如imagemodel,textmodel和regionmodel也都是基于smil标准的,比如它们分别 对应smil文档中的标签img, text和region。
当然,这都是具体的终端应用的实现,可能不同的应用会有不同的方式,但发送出去的和接收到的都应该是标准的pdu,而smil文档仅是一个其中一个pdupart而已。