这篇写彩信发送过程。
我想追踪的内容是:用户按下发送之后,彩信的图片阿数据阿文件阿,是怎么包装起来,最后发送出去。
按我看源码的先后顺序来写了。 写完可能最后整理下。
1. com.android.mms.data.WorkingMessage.java 类
send()函数。 注释如下:
/**
* Send this message over the network. Will call back with onMessageSent()
* once it has been dispatched to the telephony stack. This WorkingMessage
* object is no longer useful after this method has been called.
*/
这个是2.1的源码
public void send() {
if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
LogTag.debug("send");
}
// Get ready to write to disk.
prepareForSave(true /* notify */);
// We need the recipient list for both SMS and MMS.
final Conversation conv = mConversation;
String msgTxt = mText.toString();
if (requiresMms() || addressContainsEmailToMms(conv, msgTxt)) {
// Make local copies of the bits we need for sending a message,
// because we will be doing it off of the main thread, which will
// immediately continue on to resetting some of this state.
final Uri mmsUri = mMessageUri;
final PduPersister persister = PduPersister
.getPduPersister(mContext);
final SlideshowModel slideshow = mSlideshow;
final SendReq sendReq = makeSendReq(conv, mSubject);
// Make sure the text in slide 0 is no longer holding onto a
// reference to the text
// in the message text box.
slideshow.prepareForSend();
// Do the dirty work of sending the message off of the main UI
// thread.
new Thread(new Runnable() {
public void run() {
sendMmsWorker(conv, mmsUri, persister, slideshow, sendReq);
}
}).start();
} else {
// Same rules apply as above.
final String msgText = mText.toString();
new Thread(new Runnable() {
public void run() {
sendSmsWorker(conv, msgText);
}
}).start();
}
// update the Recipient cache with the new to address, if it's different
RecipientIdCache
.updateNumbers(conv.getThreadId(), conv.getRecipients());
// Mark the message as discarded because it is "off the market" after
// being sent.
mDiscarded = true;
}
粗浅的解说一下,
(1) prapareForSave. 先确保有slidshow,也就是实质内容。 确保文字已拷贝。确保标题。
(2a) 根据消息分类,如果是短信直接起一个线程,跑sendSmsWorker函数,发送短信
(2b) 如果是彩信,先跑这么个函数,确保文本信息
// Make sure the text in slide 0 is no longer holding onto a // reference to the text // in the message text box.
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函数之一就算发送信息成功了。
最后修改Recipient cache, 重置标志位,过程就结束了。
2。函数 sendMmsWorker
private void sendMmsWorker(Conversation conv, Uri mmsUri,
PduPersister persister, SlideshowModel slideshow, SendReq sendReq) {
// First make sure we don't have too many outstanding unsent message.
Cursor cursor = 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) {
long maxMessageSize = MmsConfig
.getMaxSizeScaleForPendingMmsAllowed()
* MmsConfig.getMaxMessageSize();
long totalPendingSize = 0;
while (cursor.moveToNext()) {
totalPendingSize += cursor.getLong(MMS_MESSAGE_SIZE_INDEX);
}
if (totalPendingSize >= maxMessageSize) {
unDiscard(); // it wasn't successfully sent. Allow it to be
// saved as a draft.
mStatusListener.onMaxPendingMessagesReached();
return;
}
}
} finally {
if (cursor != null) {
cursor.close();
}
}
mStatusListener.onPreMessageSent();
// Make sure we are still using the correct thread ID for our
// recipient set.
long threadId = conv.ensureThreadId();
if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
LogTag.debug("sendMmsWorker: update draft MMS message " + mmsUri);
}
if (mmsUri == null) {
// Create a new MMS message if one hasn't been made yet.
mmsUri = createDraftMmsMessage(persister, sendReq, slideshow);
} else {
// Otherwise, sync the MMS message in progress to disk.
updateDraftMmsMessage(mmsUri, persister, slideshow, sendReq);
}
// Be paranoid and clean any draft SMS up.
deleteDraftSmsMessage(threadId);
MessageSender sender = new MmsMessageSender(mContext, mmsUri, slideshow
.getCurrentMessageSize());
try {
if (!sender.sendMessage(threadId)) {
// The message was sent through SMS protocol, we should
// delete the copy which was previously saved in MMS drafts.
SqliteWrapper.delete(mContext, mContentResolver, mmsUri, null,
null);
}
// Make sure this thread isn't over the limits in message count
Recycler.getMmsRecycler().deleteOldMessagesByThreadId(mContext,
threadId);
} catch (Exception e) {
Log.e(TAG, "Failed to send message: " + 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,这个函数功能是重新显示conversation list。
3 MmsMessageSender.java类。在mms/transaction下面。实现了MessageSender接口。这个接口只有一个事儿,就是sendMessage并返回boolean的值。弱发送的是mms,返回true。若发送的是sms,返回false。出错返回啥?exception。
我最先想要追踪的发送流程也在这里了。贴一些代码
public MmsMessageSender(Context context, Uri location, long messageSize) {
mContext = context;
mMessageUri = location;
mMessageSize = messageSize;
if (mMessageUri == null) {
throw new IllegalArgumentException("Null message URI.");
}
}
public boolean sendMessage(long token) throws MmsException {
// Load the MMS from the message uri
PduPersister p = PduPersister.getPduPersister(mContext);
GenericPdu pdu = p.load(mMessageUri);
if (pdu.getMessageType() != PduHeaders.MESSAGE_TYPE_SEND_REQ) {
throw new MmsException("Invalid message: " + pdu.getMessageType());
}
SendReq sendReq = (SendReq) pdu;
// Update headers.
updatePreferencesHeaders(sendReq);
// MessageClass.
sendReq.setMessageClass(DEFAULT_MESSAGE_CLASS.getBytes());
// Update the 'date' field of the message before sending it.
sendReq.setDate(System.currentTimeMillis() / 1000L);
sendReq.setMessageSize(mMessageSize);
p.updateHeaders(mMessageUri, sendReq);
// Move the message into MMS Outbox
p.move(mMessageUri, Mms.Outbox.CONTENT_URI);
// Start MMS transaction service
SendingProgressTokenManager
.put(ContentUris.parseId(mMessageUri), token);
mContext.startService(new Intent(mContext, TransactionService.class));
return true;
}
解说:
现从PduPersister那里拿数据,包括需要拼装的发送报头和需要发送的数据信息。
然后把要发送的信息相关数据从数据库的draft那里转移到send,表示已经发送。
最后起一个TransactionService服务,这个服务也是从PduPersister里找,找到需要发送的数据,并通过不同的用户网络送出去。
这块我猜一般人都没有改的需求。。
4.
createDraftMmsMessage(persister, sendReq, slideshow); 和
updateDraftMmsMessage(mmsUri, persister, slideshow, sendReq); 这两个函数
刨掉 try catch , createDraftMmsMessage 函数大概有这么几句:
PduBody pb = slideshow.toPduBody();
sendReq.setBody(pb);
Uri res = persister.persist(sendReq, Mms.Draft.CONTENT_URI);
slideshow.sync(pb);
updateDraftMmsMessage 函数大概有这么几句:
persister.updateHeaders(uri, sendReq);
final PduBody pb = 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();里面,用SMILDocument mDocumentCache;
调用到SlideshowModel.java的
//其中context=null。 isMakingCopy=false。 document=mDocumentCache
private PduBody makePduBody(Context context, SMILDocument document,
boolean isMakingCopy) {
PduBody pb = new PduBody();
boolean hasForwardLock = false;
for (SlideModel slide : mSlides) {
for (MediaModel media : slide) {
if (isMakingCopy) {
if (media.isDrmProtected() && !media.isAllowedToForward()) {
hasForwardLock = true;
continue;
}
}
PduPart part = new PduPart();
if (media.isText()) {
TextModel text = (TextModel) media;
// Don't create empty text part.
if (TextUtils.isEmpty(text.getText())) {
continue;
}
// Set Charset if it's a text media.
part.setCharset(text.getCharset());
}
// Set Content-Type.
part.setContentType(media.getContentType().getBytes());
String src = media.getSrc();
String location;
boolean startWithContentId = src.startsWith("cid:");
if (startWithContentId) {
location = src.substring("cid:".length());
} else {
location = src;
}
// Set Content-Location.
part.setContentLocation(location.getBytes());
// Set Content-Id.
if (startWithContentId) {
// Keep the original Content-Id.
part.setContentId(location.getBytes());
} else {
int index = location.lastIndexOf(".");
String contentId = (index == -1) ? location : location
.substring(0, index);
part.setContentId(contentId.getBytes());
}
if (media.isDrmProtected()) {
DrmWrapper wrapper = media.getDrmObject();
part.setDataUri(wrapper.getOriginalUri());
part.setData(wrapper.getOriginalData());
} else if (media.isText()) {
part.setData(((TextModel) media).getText().getBytes());
} else if (media.isImage() || media.isVideo()
|| media.isAudio()) {
part.setDataUri(media.getUri());
} else {
Log.w(TAG, "Unsupport media: " + 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);
}
// Create and insert SMIL part(as the first part) into the PduBody.
ByteArrayOutputStream out = new ByteArrayOutputStream();
SmilXmlSerializer.serialize(document, out);
PduPart smilPart = new PduPart();
smilPart.setContentId("smil".getBytes());
smilPart.setContentLocation("smil.xml".getBytes());
smilPart.setContentType(ContentType.APP_SMIL.getBytes());
smilPart.setData(out.toByteArray());
pb.addPart(0, smilPart);
return pb;
}
好了,齐活儿了
顺序有点乱。抱歉。。。
分享到:
相关推荐
Android MMS 彩信发送代码流程详解 Android MMS 彩信发送代码流程是 Android 操作系统中一个复杂的过程,涉及到多个应用层和系统服务的交互。下面将对该过程进行详细解释,从 Messaging 应用层到 framework 应用层...
【彩信发送开发包 apimms】是一款专用于发送彩信的软件开发工具,它提供了API接口,使得开发者能够方便地在自己的应用程序中集成彩信发送功能。在IT行业中,彩信(Multimedia Messaging Service,简称MMS)是一种比...
**MM7彩信发送程序详解** MM7彩信发送程序是一种基于移动通信网络的多媒体消息服务(MMS)协议,用于在移动设备之间传递多媒体内容,如图片、音频、视频等。这一技术主要应用于Java平台,使得开发者可以构建能够...
彩信模拟器 可用来模拟发送彩信
本主题将深入探讨如何在Android应用中实现彩信发送功能,基于源码的角度来解析整个流程。 1. **彩信发送流程** 在Android中,发送彩信主要涉及` MultimediaMessageService` 和 `MMSTransport` 类。首先,你需要...
最简单的android彩信发送代码。适合学习彩信发送流程。 事实上android中的Mms模块中,对这个流程进行了很少的封装,这里就是将其简化,帮助初学者了解流程。
MS/MM1模拟器主要是模拟使用MM1协议的彩信中心。此类型彩信中心使用基于类似二进制HTTP协议的通讯模式。本次所发布的MM1模拟器采用了SimpleTeam自行开发的MM1/WSP协议栈。该模拟器是彩信中心的一个全真模拟软件,...
### Android彩信发送机制深入解析 在探讨Android平台上的彩信发送流程时,我们主要关注的是用户触发发送操作后,如何将包含图片、数据、文件等多媒体内容的彩信进行封装并最终通过网络发送出去的过程。本文将基于`...
本文将详细讲解如何使用C#实现彩信发送的简单实例程序,以及涉及的关键知识点。 首先,我们需要理解彩信(Multimedia Messaging Service, 简称MMS)的工作原理。彩信是短信服务的升级版,允许用户发送包含多媒体...
短信和彩信发送接口是现代通信技术中的一个重要组成部分,尤其在移动应用开发和服务提供商中广泛使用。这样的接口允许开发者通过编程方式向用户发送文本或多媒体信息,极大地提升了用户体验和服务效率。下面将详细...
从提供的文件名来看,`android自定义发彩信实现(不调用系统发彩信界面) - Android移动开发技术文章_手机开发 - 红黑联盟.mht`可能包含一篇详细的文章,解释了如何实现自定义的彩信发送而不调用系统的彩信界面。...
ASP.NET彩信发送接收项目是基于C#编程语言开发的一个应用程序,主要功能是实现彩信的发送和接收。在这个项目中,开发者需要处理的关键技术点包括但不限于以下几方面: 1. **ASP.NET框架**:ASP.NET是微软提供的一个...
本文将深入探讨如何使用C语言通过GPRS模块实现彩信发送的功能。 首先,我们需要理解GPRS(General Packet Radio Service)是一种移动通信技术,它允许设备通过移动网络进行分组数据传输。GPRS模块是连接设备与GPRS...
"symbian彩信发送源代码.rar"这个压缩包文件显然包含了用于在Symbian系统上实现彩信发送功能的源代码。这里我们将深入探讨Symbian操作系统、彩信技术以及如何在该平台上开发和实现彩信发送功能。 首先,Symbian操作...
【标题】"java源码:彩信发送开发包 apimms.rar" 提供的是一个用于在Java环境中开发彩信发送功能的源代码包。彩信(Multimedia Messaging Service,MMS)是一种允许用户发送和接收包含多媒体内容如图片、音频和视频...
- 终端设备通过无线网络将彩信发送到MMSC。 - MMSC接收到彩信后进行必要的格式转换,并存储彩信。 - **接收端操作**: - MMSC尝试将彩信发送给接收方终端设备。 - 如果接收方终端设备当前不可用(如关机或不在...
Java彩信发送开发包,通常是指用于在Java环境中构建应用程序,以便向移动设备发送多媒体信息(MMS)的工具集。MMS与SMS(文本消息)不同,它允许用户发送和接收包括图片、音频和视频在内的多媒体内容。在Java平台上...
2. **MMS服务器**:在MMS彩信发送过程中,手机客户端首先将多媒体内容上传到MMS代理服务器或MMSC(MMS中心)。这个过程可能涉及到身份验证、内容编码和大小限制等问题。然后,MMSC负责存储、路由和转发彩信到目标...
《电信设备-彩信发送方法》 彩信(Multimedia Messaging Service,简称MMS)是移动通信领域中一种用于发送多媒体内容的技术,包括文本、图片、音频和视频等。彩信与普通短信(Short Message Service,SMS)相比,...
【Java彩信发送开发包详解】 在Java编程领域,彩信(Multimedia Messaging Service, MMS)发送是一项常见的通信需求,常用于企业级应用或移动应用中,如发送包含图片、音频、视频等多媒体内容的消息。`apimms.zip`...