`
mars5337
  • 浏览: 89368 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Android彩信发送过程介绍

阅读更多
这篇写彩信发送过程。
我想追踪的内容是:用户按下发送之后,彩信的图片阿数据阿文件阿,是怎么包装起来,最后发送出去。
按我看源码的先后顺序来写了。 写完可能最后整理下。
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的源码
Java代码
复制到剪贴板  Java代码
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 
Java代码
复制到剪贴板  Java代码
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。
我最先想要追踪的发送流程也在这里了。贴一些代码
Java代码
复制到剪贴板  Java代码
public MmsMessageSender(Context context, Uri location, long messageSize) {    
    mContext = context;    
    mMessageUri = location;    
    mMessageSize = messageSize;    
        
    if (mMessageUri == null) {    
        throw new IllegalArgumentException("Null message URI.");    
    }    
}    
 
 
Java代码   
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 函数大概有这么几句:
复制到剪贴板  Java代码
Java代码   
PduBody pb = slideshow.toPduBody();    
sendReq.setBody(pb);    
Uri res = persister.persist(sendReq, Mms.Draft.CONTENT_URI);    
slideshow.sync(pb);    
 
updateDraftMmsMessage 函数大概有这么几句:   
Java代码   
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的
Java代码
复制到剪贴板  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彩信发送介绍

    ### Android彩信发送机制深入解析 在探讨Android平台上的彩信发送流程时,我们主要关注的是用户触发发送操作后,如何将包含图片、数据、文件等多媒体内容的彩信进行封装并最终通过网络发送出去的过程。本文将基于`...

    【Android】Android彩信发送源码

    总的来说,Android彩信发送涉及多个组件和步骤,需要理解MMS协议,掌握网络编程,以及良好的异常处理和用户交互设计。通过阅读和分析源码,可以更深入地理解这个过程,并为自己的应用实现高效且稳定的彩信发送功能。

    Android MMS彩信发送代码流程+UML流程图

    Android MMS 彩信发送代码流程是 Android 操作系统中一个复杂的过程,涉及到多个应用层和系统服务的交互。下面将对该过程进行详细解释,从 Messaging 应用层到 framework 应用层的实现细节。 一、Messaging 应用层 ...

    android 发送彩信

    通过分析和运行这个项目,你可以更深入地了解Android彩信发送的实现细节。 请注意,由于Android系统的碎片化,不同设备和Android版本可能对彩信的支持有所不同。有些设备可能不允许程序自动发送彩信,或者需要用户...

    android 彩信 自定义

    总的来说,自定义Android彩信发送功能涉及到Android的多方面知识,包括权限管理、数据库操作、网络通信以及UI设计。理解并实践这些知识点,不仅可以帮助开发者创建一个功能完备的彩信应用,还能提升他们对Android...

    android发送彩信

    在Android平台上,发送彩信(Multimedia Messaging Service, MMS)是一项相对复杂的功能,涉及到多媒体内容的封装、网络通信以及与MMS服务器的交互。本文将深入探讨Android发送彩信的相关知识点,帮助开发者理解并...

    android向db插入短信及彩信数据

    在Android平台上,管理和操作数据库是常见的任务之一,特别是在涉及到用户数据存储时,如短信和彩信。本知识点将深入探讨如何向Android系统的...理解并掌握这一过程,对于开发涉及通信功能的Android应用至关重要。

    简单掌握Android开发中彩信的发送接收及其附件的处理

    一、彩信发送 发送彩信的过程相对复杂,主要涉及到以下几个关键步骤: 1. 准备工作:在`sendMmsWorker`函数中,需要检查对话历史,确保没有未发送的消息,并设置`MessageStatusListener`监听消息状态的变化。`...

    后台彩信发送

    从提供的文件名来看,`android自定义发彩信实现(不调用系统发彩信界面) - Android移动开发技术文章_手机开发 - 红黑联盟.mht`可能包含一篇详细的文章,解释了如何实现自定义的彩信发送而不调用系统的彩信界面。...

    Android截取彩信总结

    - Android系统版本的不同可能会导致API的变化,因此在开发过程中需要注意兼容性问题。 #### 结论 通过对Android系统中短信与彩信的管理机制的理解,我们可以更加有效地实现彩信的接收、拦截和处理等功能。这对于...

    安卓短信彩信相关相关-点对点发送彩信源码.rar

    2. **Android彩信API**: 安卓系统为开发者提供了处理短信和彩信的API,主要包含在`android.telephony`和`android.telephony.mms`包中。例如,`SmsManager`类用于管理短信的发送,而`MmsService`和`PduBuilder`等类...

    MMS发送流程(代码版)android

    Android 操作系统中,MMS 发送流程是一个复杂的过程,涉及到多个组件和类的交互。在本文中,我们将对 Android 2.2 版本中的 MMS 发送流程进行详细的分析。 点击发送按钮 在 ComposeMessageActivity 中,点击发送...

    基于Android技术的彩信隐写通信系统研究.pdf

    7. **彩信发送流程优化**:系统优化了彩信发送流程,提高了传输效率。 【标签】中的知识点: 1. **Android客户端应用开发**:指在Android平台上开发应用程序,这里特别指代的是彩信隐写通信系统的客户端部分。 2. ...

    彩信协议1.2

    彩信协议1.2是针对手机彩信模块开发的重要参考资料,它详细定义了移动设备如何发送、接收和处理多媒体消息(MMS)的规范。在本文中,我们将深入探讨彩信协议1.2的核心概念、功能以及其在手机通信中的应用。 1. **...

    Android 短信转换成彩信的消息数量(实例代码)

    在 compose_message_activity.xml 布局文件中,我们可以看到短信输入框、剩余字数显示框、短信发送按钮和彩信发送按钮的定义。 在 ComposeMessageActivity.java 文件中,我们可以看到 updateCounter 方法的实现。该...

    一种基于Android智能终端的彩信取证分析方法.pdf

    综上所述,基于Android智能终端的彩信取证分析是一个涉及数据恢复、文件解析、信息安全等多个领域的复杂过程。通过深入研究和开发,可以构建更高效、更全面的取证方案,有效地服务于司法调查和网络安全维护。

    mms_send.rar_MMS_彩信发送

    2. **MMS服务器**:在MMS彩信发送过程中,手机客户端首先将多媒体内容上传到MMS代理服务器或MMSC(MMS中心)。这个过程可能涉及到身份验证、内容编码和大小限制等问题。然后,MMSC负责存储、路由和转发彩信到目标...

    基于Android智能手机的彩信加密与传输机制研究.pdf

    《基于Android智能手机的彩信加密与传输机制研究》这篇论文主要探讨了在移动通信领域,尤其是在Android智能手机环境下,如何确保彩信(MMS)的安全传输。随着3G和4G技术的发展,智能手机用户日益增多,彩信作为一种...

    安卓短信彩信相关相关-android实现发送验证码倒计时.rar

    这个压缩包“安卓短信彩信相关-android实现发送验证码倒计时.rar”可能包含了实现这一功能的相关代码示例。 发送验证码的过程通常包括以下几个步骤: 1. **生成验证码**:系统通过随机算法生成一个唯一的数字或...

    安卓短信彩信相关相关-android.mac.ios平台的自动返回短信验证码接口java.zip

    从标签“短信彩信相关”可以推断,这个压缩包中的内容主要涉及移动通信中的短信和彩信服务,可能包括了发送、接收、验证短信验证码的过程,甚至可能有处理彩信的部分。在实际应用中,短信验证码常用于用户注册、登录...

Global site tag (gtag.js) - Google Analytics