Android彩信发送过程介绍

这篇写彩信发送过程。

我想追踪的内容是:用户按下发送之后,彩信的图片阿数据阿文件阿,是怎么包装起来,最后发送出去。

按我看源码的先后顺序来写了。写完可能最后整理下。

1.com.Android.mms.data.WorkingMessage.Java类

send()函数。注释如下:

/**

*Sendthismessageoverthenetwork.WillcallbackwithonMessageSent()

*onceithasbeendispatchedtothetelephonystack.ThisWorkingMessage

*objectisnolongerusefulafterthismethodhasbeencalled.

*/

这个是2.1的源码

Java代码

复制到剪贴板Java代码

publicvoidsend(){

if(Log.isLoggable(LogTag.TRANSACTION,Log.VERBOSE)){

LogTag.debug("send");

}

//Getreadytowritetodisk.

prepareForSave(true/*notify*/);

//WeneedtherecipientlistforbothSMSandMMS.

finalConversationconv=mConversation;

StringmsgTxt=mText.toString();

if(requiresMms()||addressContainsEmailToMms(conv,msgTxt)){

//Makelocalcopiesofthebitsweneedforsendingamessage,

//becausewewillbedoingitoffofthemainthread,whichwill

//immediatelycontinueontoresettingsomeofthisstate.

finalUrimmsUri=mMessageUri;

finalPduPersisterpersister=PduPersister

.getPduPersister(mContext);

finalSlideshowModelslideshow=mSlideshow;

finalSendReqsendReq=makeSendReq(conv,mSubject);

//Makesurethetextinslide0isnolongerholdingontoa

//referencetothetext

//inthemessagetextbox.

slideshow.prepareForSend();

//DothedirtyworkofsendingthemessageoffofthemainUI

//thread.

newThread(newRunnable(){

publicvoidrun(){

sendMmsWorker(conv,mmsUri,persister,slideshow,sendReq);

}

}).start();

}else{

//Samerulesapplyasabove.

finalStringmsgText=mText.toString();

newThread(newRunnable(){

publicvoidrun(){

sendSmsWorker(conv,msgText);

}

}).start();

}

//updatetheRecipientcachewiththenewtoaddress,ifit'sdifferent

RecipientIdCache

.updateNumbers(conv.getThreadId(),conv.getRecipients());

//Markthemessageasdiscardedbecauseitis"offthemarket"after

//beingsent.

mDiscarded=true;

}

粗浅的解说一下,

(1)prapareForSave.先确保有slidshow,也就是实质内容。确保文字已拷贝。确保标题。

(2a)根据消息分类,如果是短信直接起一个线程,跑sendSmsWorker函数,发送短信

(2b)如果是彩信,先跑这么个函数,确保文本信息

//Makesurethetextinslide0isnolongerholdingontoa//referencetothetext//inthemessagetextbox.

slideshow.prepareForSend();

TheCranberriers(卡百利)的歌真好听。

然后起一个线程,单独跑sendMmsWorker函数,后文有介绍。

彩信比sms麻烦很多。从sendMmsWorker函数的参数就可以看出来:(conv,mmsUri,persister,slideshow,sendReq)上下文,uri,PduPersister(彩信是用pdu的),slideshow包含了所有的彩信信息,sendreq包含了mime封装mms时的headers(在我的剥壳彩信2里面有提到)。包括了ContentType("application/vnd.wap.multipart.related",from,to等信息。

(3)。不管是短信还是彩信,起了那俩个worker函数之一就算发送信息成功了。

最后修改Recipientcache,重置标志位,过程就结束了。

2。函数sendMmsWorker

Java代码

复制到剪贴板Java代码

privatevoidsendMmsWorker(Conversationconv,UrimmsUri,

PduPersisterpersister,SlideshowModelslideshow,SendReqsendReq){

//Firstmakesurewedon'thavetoomanyoutstandingunsentmessage.

Cursorcursor=null;

try{

Log.d("GN@@@","mContext:"+mContext.toString());

Log.d("GN@@@","mContentResolver:"+mContentResolver.toString());

Log.d("GN@@@","Mms.Outbox.CONTENT_URI:"+Mms.Outbox.CONTENT_URI.toString());

cursor=SqliteWrapper.query(mContext,mContentResolver,

Mms.Outbox.CONTENT_URI,MMS_OUTBOX_PROJECTION,null,null,

null);

if(cursor!=null){

longmaxMessageSize=MmsConfig

.getMaxSizeScaleForPendingMmsAllowed()

*MmsConfig.getMaxMessageSize();

longtotalPendingSize=0;

while(cursor.moveToNext()){

totalPendingSize+=cursor.getLong(MMS_MESSAGE_SIZE_INDEX);

}

if(totalPendingSize>=maxMessageSize){

unDiscard();//itwasn'tsuccessfullysent.Allowittobe

//savedasadraft.

mStatusListener.onMaxPendingMessagesReached();

return;

}

}

}finally{

if(cursor!=null){

cursor.close();

}

}

mStatusListener.onPreMessageSent();

//MakesurewearestillusingthecorrectthreadIDforour

//recipientset.

longthreadId=conv.ensureThreadId();

if(Log.isLoggable(LogTag.APP,Log.VERBOSE)){

LogTag.debug("sendMmsWorker:updatedraftMMSmessage"+mmsUri);

}

if(mmsUri==null){

//CreateanewMMSmessageifonehasn'tbeenmadeyet.

mmsUri=createDraftMmsMessage(persister,sendReq,slideshow);

}else{

//Otherwise,synctheMMSmessageinprogresstodisk.

updateDraftMmsMessage(mmsUri,persister,slideshow,sendReq);

}

//BeparanoidandcleananydraftSMSup.

deleteDraftSmsMessage(threadId);

MessageSendersender=newMmsMessageSender(mContext,mmsUri,slideshow

.getCurrentMessageSize());

try{

if(!sender.sendMessage(threadId)){

//ThemessagewassentthroughSMSprotocol,weshould

//deletethecopywhichwaspreviouslysavedinMMSdrafts.

SqliteWrapper.delete(mContext,mContentResolver,mmsUri,null,

null);

}

//Makesurethisthreadisn'toverthelimitsinmessagecount

Recycler.getMmsRecycler().deleteOldMessagesByThreadId(mContext,

threadId);

}catch(Exceptione){

Log.e(TAG,"Failedtosendmessage:"+mmsUri+",threadid="

+threadId,e);

}

mStatusListener.onMessageSent();

}

依旧是粗浅的解说:

a)前面挺长一段代码,检查这个对话(conversation)之前还有没有未发送的信息,uri是Mms.Outbox.CONTENT_URI。

这里需要提到一下MessageStatusListener,这个Interface接口实现在WorkingMessage.java里,而短信类的主题ComposeMessageActivity.java实现了这个接口,所以前者在一些状态改变的时候可以很方便的调用后者的一些函数,作相应的改动。主要是:onProtocolChanged彩信短信互切换,onAttachmentChanged福建改变,onPreMessageSent发消息前,onMessageSent发消息后。

b)

当然,这里调用了onPreMessageSent这个监听函数,

然后ComposeMessageActivity就会调用resetMessage函数,这个函数会调整显示,focus,软键盘等等。

c)

然后检查mmsUri。如果这个uri是空的话,直接造一个新的uri继续发送。这个真是让我叫亚灭爹。因为一开始不知道

这个createDraftMmsMessage(persister,sendReq,slideshow);函数可以包含所有发送需要的信息,以为这么发出去太可怕了。

如果uri不为空。调用的是updateDraftMmsMessage(mmsUri,persister,slideshow,sendReq);

总之功能是,把这个将发送的mms,存disk了,也就是存draft了。为什么要发送还要存draft呢,后面另会说,因为这个是我写这个文章前想要找的东西。。。这个过程还有一些信息写道mmsUri了。所以之后mmsUri就可以代表将发送的mms的全部信息。

d)deleteDraftSmsMessage删除草稿

e)创建一个MmsMessageSender,用这个sender来调用sendMessage函数

可以猜到的,Sms那边是SmsMessageSender,同样调用sendMessage函数

通过这里之后,短信已经真的发掉了。这个类后面有介绍。

f)这里这个if相当搞笑,按正常流程下来,按理这里本来这里是一个彩信的发送,然后有一些数据在draft数据库,会在上面的流程中被移到send数据库。

但是搞笑的地方来了:因为忽然发现函数返回值表示刚刚发送出去的其实是一个短信sms,而已。于是要把数据库里存着的draft删掉。

我也不知道这个if里面的情况会不会发生,反正源码是这么写的,我只管不负责任直译。。。

g)调用onMessageSent这个监听函数。调用ComposeMessageActivity的onMessageSent,这个函数功能是重新显示conversationlist。

3MmsMessageSender.java类。在mms/transaction下面。实现了MessageSender接口。这个接口只有一个事儿,就是sendMessage并返回boolean的值。弱发送的是mms,返回true。若发送的是sms,返回false。出错返回啥?exception。

我最先想要追踪的发送流程也在这里了。贴一些代码

Java代码

复制到剪贴板Java代码

publicMmsMessageSender(Contextcontext,Urilocation,longmessageSize){

mContext=context;

mMessageUri=location;

mMessageSize=messageSize;

if(mMessageUri==null){

thrownewIllegalArgumentException("NullmessageURI.");

}

}

Java代码

publicbooleansendMessage(longtoken)throwsMmsException{

//LoadtheMMSfromthemessageuri

PduPersisterp=PduPersister.getPduPersister(mContext);

GenericPdupdu=p.load(mMessageUri);

if(pdu.getMessageType()!=PduHeaders.MESSAGE_TYPE_SEND_REQ){

thrownewMmsException("Invalidmessage:"+pdu.getMessageType());

}

SendReqsendReq=(SendReq)pdu;

//Updateheaders.

updatePreferencesHeaders(sendReq);

//MessageClass.

sendReq.setMessageClass(DEFAULT_MESSAGE_CLASS.getBytes());

//Updatethe'date'fieldofthemessagebeforesendingit.

sendReq.setDate(System.currentTimeMillis()/1000L);

sendReq.setMessageSize(mMessageSize);

p.updateHeaders(mMessageUri,sendReq);

//MovethemessageintoMMSOutbox

p.move(mMessageUri,Mms.Outbox.CONTENT_URI);

//StartMMStransactionservice

SendingProgressTokenManager

.put(ContentUris.parseId(mMessageUri),token);

mContext.startService(newIntent(mContext,TransactionService.class));

returntrue;

}

解说:

现从PduPersister那里拿数据,包括需要拼装的发送报头和需要发送的数据信息。

然后把要发送的信息相关数据从数据库的draft那里转移到send,表示已经发送。

最后起一个TransactionService服务,这个服务也是从PduPersister里找,找到需要发送的数据,并通过不同的用户网络送出去。

这块我猜一般人都没有改的需求。。

4.

createDraftMmsMessage(persister,sendReq,slideshow);和

updateDraftMmsMessage(mmsUri,persister,slideshow,sendReq);这两个函数

刨掉trycatch,createDraftMmsMessage函数大概有这么几句:

复制到剪贴板Java代码

Java代码

PduBodypb=slideshow.toPduBody();

sendReq.setBody(pb);

Urires=persister.persist(sendReq,Mms.Draft.CONTENT_URI);

slideshow.sync(pb);

updateDraftMmsMessage函数大概有这么几句:

Java代码

persister.updateHeaders(uri,sendReq);

finalPduBodypb=slideshow.toPduBody();

persister.updateParts(uri,pb);

slideshow.sync(pb);

两个函数从本质上讲是一样的:把附件的东西以pdubody的形式存下来,另外就是更新uri。

什么叫PduBody呢?厉害了。就是n个PduPart。什么叫PduPart呢?厉害了,就是数据库里的那个Part!那个part是什么?

那个最厉害了。数据库里的PART_1234455这种数据,文件名代表创建时间(在mediaModel产生时就进系统了),导出来就是源文件,比如图片文件,改个jpg就可以看了。

sync函数不怎么动,无责任解说:把每个slide里面每个媒体跟真实文件位置对应上。

slideshow.toPduBody();里面,用SMILDocumentmDocumentCache;

调用到SlideshowModel.java的

Java代码

复制到剪贴板Java代码

//其中context=null。isMakingCopy=false。document=mDocumentCache

privatePduBodymakePduBody(Contextcontext,SMILDocumentdocument,

booleanisMakingCopy){

PduBodypb=newPduBody();

booleanhasForwardLock=false;

for(SlideModelslide:mSlides){

for(MediaModelmedia:slide){

if(isMakingCopy){

if(media.isDrmProtected()&&!media.isAllowedToForward()){

hasForwardLock=true;

continue;

}

}

PduPartpart=newPduPart();

if(media.isText()){

TextModeltext=(TextModel)media;

//Don'tcreateemptytextpart.

if(TextUtils.isEmpty(text.getText())){

continue;

}

//SetCharsetifit'satextmedia.

part.setCharset(text.getCharset());

}

//SetContent-Type.

part.setContentType(media.getContentType().getBytes());

Stringsrc=media.getSrc();

Stringlocation;

booleanstartWithContentId=src.startsWith("cid:");

if(startWithContentId){

location=src.substring("cid:".length());

}else{

location=src;

}

//SetContent-Location.

part.setContentLocation(location.getBytes());

//SetContent-Id.

if(startWithContentId){

//KeeptheoriginalContent-Id.

part.setContentId(location.getBytes());

}else{

intindex=location.lastIndexOf(".");

StringcontentId=(index==-1)?location:location

.substring(0,index);

part.setContentId(contentId.getBytes());

}

if(media.isDrmProtected()){

DrmWrapperwrapper=media.getDrmObject();

part.setDataUri(wrapper.getOriginalUri());

part.setData(wrapper.getOriginalData());

}elseif(media.isText()){

part.setData(((TextModel)media).getText().getBytes());

}elseif(media.isImage()||media.isVideo()

||media.isAudio()){

part.setDataUri(media.getUri());

}else{

Log.w(TAG,"Unsupportmedia:"+media);

}

pb.addPart(part);

}

}

if(hasForwardLock&&isMakingCopy&&context!=null){

Toast.makeText(context,

context.getString(R.string.cannot_forward_drm_obj),

Toast.LENGTH_LONG).show();

document=SmilHelper.getDocument(pb);

}

//CreateandinsertSMILpart(asthefirstpart)intothePduBody.

ByteArrayOutputStreamout=newByteArrayOutputStream();

SmilXmlSerializer.serialize(document,out);

PduPartsmilPart=newPduPart();

smilPart.setContentId("smil".getBytes());

smilPart.setContentLocation("smil.xml".getBytes());

smilPart.setContentType(ContentType.APP_SMIL.getBytes());

smilPart.setData(out.toByteArray());

pb.addPart(0,smilPart);

returnpb;

}

好了,搞定

相关推荐