android 发送 SMS 详解

来源:互联网 发布:java.io jar包 编辑:程序博客网 时间:2024/04/30 21:59

    这两天,闲来无事,把发送SMS 的流程梳理了一遍,有很大收获, 在此记录下来,供大家参考.

     1, ComposeMessageActivity.java

          (1) 在 onclick 方法中,首先判断是否准备好发送,

              isPreparedForSending()方法,判断: 收件人个数大于1;短信有内容(对于彩信来说 , 有附件\有主題\短信内容超过7条) .

        其次,如果在多卡的情况下, 判断当前是用哪个卡发送.彩信的情况,需要判断哪个卡是默认的卡.如果不是用默认的卡发送彩信的话,需要切换数据通道.

         (2)在confirmSendMessageIfNeeded()中 .

            当在已有会话中发送,直接掉用sendMessage(true); 如果是新建的一个会话 ,需要判断是否有不合法的收件人.然后再调用 sendMessage(true);

       (3)sendMessage(boolean bCheckEcmMode) 

            首先通过mConversation.getRecipients().serialize()获取收件人列表。 然后调用

                   mWorkingMessage.send(mDebugRecipients); 去调用WorkingMessage.java中的send(...)函数去发送。

         在ComposeMessageActivity.java 中,短信发送之前的准备工作基本就是这些, 当然,也可以根据交互设计去订制一些规则或者一些处理。

    2,WorkingMessage.java

       (1), send() . 

              首先,调用prepareForSave(...) 去保存一些基本参数。 我们来看看里面都作了哪些工作, syncWorkingRecipients(), 里面是保存会话收件人列表。

              如果是Mms 的话,要保证有一个SlideshowModel,因为大家都知道,所有的彩信都需要有个SlideshowModel; syncTextToSlideshow(),给出的解释是 Moves the message text into the slideshow. ,总的来说就是创建一个TextModel,把输入框里面的文本存储在 text_0.txt 中。具体的细节在这里不讨论,等以后有机会细说。

             然后,在一系列赋值操作后,就要判断发送的是短信还是彩信,if (requiresMms() || addressContainsEmailToMms(conv, msgTxt)) ,这里特别说明一下,android支持以彩信形式发送邮件,所以,当检测到收件人列表里面有包括email 的话,会以彩信的形式发送。下面我们直接进入发送短信的代码块。

             在执行发送短信进程之前,会检测是否设置短信签名,如果设置了, 就把短信签名加到短信内容中去。后面,启动一个进程,调用preSendSmsWorker(...) , 为什么要新启动一个进程去执行呢,个人肤浅的认为是由于发送短信是一个相对来说比较耗时的操作,为了不影响UI操作的交互性,需要单独开启一个进程去执行。

       (2), preSendSmsWorker(...)

            UserHappinessSignals.userAcceptedImeText(mActivity); 这行代码,搞不懂什么意思。

            mStatusListener.onPreMessageSent();  composemessageactivity 实现了这个接口,回调composemessageactivity 中的onPreMessageSent()函数,reset一下message状态。

            long threadId = conv.ensureThreadId(); 获取会话的id ,如果是新建的会话,则create 一个会话,并把id返回过来。

           然后就开始调用sendSmsWorker(...), 并删除短\彩信草稿。

      (3) sendSmsWorker(...)

           多卡情况下,要判断出user 希望用哪张卡去发送短信。然后通过收件人列表、短信内容、会话Id 、多卡情况下的卡id 去创建一个 SmsMessageSender对象。然后调用创建对象的sendMessage(long threadId)去发送,然后去检查是否开启自动删除旧信息,超过设置的会话信息最大条数的话,就会自动删除比较早的信息。最后,调用composemessageactivity中的onMessageSent()去刷新ui界面。

    3,SmsMessageSender.java

        (1) sendMessage(...)           queueMessage(...)

               主要实现的功能就是把短信添加到发送短信队列中去。并通知SmsReceiverService ,有信息需要发送。

    4,SmsReceiverService.java

              当接收到ACTION_SEND_MESSAGE 消息时,调用 handleSendMessage(intent),而这个函数又调用sendFirstQueuedMessage(int subId),这个函数主要是获取到发送短信队列中的第一个需要发送的短信,并创建SmsSingleRecipientSender 对象,调用其中的sendMessage(....)方法。在 catch 块中,有处理发送失败的函数messageFailedToSend()。

   5,SmsSingleRecipientSender.java

             sendMessage(...) 

                 如果发送的是一条长短信,则需要把此信息分割成几条短信;把这条信息移动到发信箱里面 ;创建deliveryIntents 和 sentIntents ,监听短信发送状态;创建SmsManager对象,并调用其中的sendMultipartTextMessage(...)函数,去发送信息。


===========至此,短信发送流程的app部分终于完成,下面进入到framework部分=======================

    6, SmsManager.java

           sendMultipartTextMessage(...) 

               sendMultipartTextMessage 中判断是发送一条短信还是多条短信, 如果是多条, 则创建一个ISms 对象,调用sendMultipartTextOnSubscription 方法进行发送;如果是一条, 则直接调用sendTextMessage(...) 。 而在 sendTextMessage(...)中,也是创建一个ISms对象,调用其中的sendTextOnSubscription方法进行发送。 下面我们来看 ISms。 ISms是以aidl文件进行定义的,文件名是ISms.aidl 。关于aidl的介绍, 大家可以参考http://blog.csdn.net/stonecao/article/details/6425019 中的描述,这里就不再赘述。这里我们主要是看继承ISms 的MSimIccSmsInterfaceManager 类。

7, MSimIccSmsInterfaceManager.java

        sendMultipartTextOnSubscription 和 sendTextOnSubscription 首先都是根据卡一卡二通过 getIccSmsInterfaceManager(int subId)来获取一个 IccSmsInterfaceManager对象。 然后用对象的 sendMultipartText 和 sendText 去发送信息。在这里,决定了发送此信息是通过卡一还是卡二。

8,IccSmsInterfaceManager.java

     在这个对象中都是调用其中的一个SMSDispatcher 对象里的对应的多条信息发送和单条信息发送对应的函数:mDispatcher.sendMultipartText() 、mDispatcher.sendText(...)。 mDispatcher 被定义成一个ImsSMSDispatcher对象。

9,ImsSMSDispatcher.java

    在对象构造的时候, 会创建CdmaSMSDispatcher 和 GsmSMSDispatcher 两个不同的对象,并监听一些广播和事件 。

    先检测需要发送的短信是用的cdms 卡还是 gsm卡 , 然后调用不同的对象的方法去实现sendText 和sendMultipartText方法。

10, CdmaSMSDispatcher\GsmSMSDispatcher 

   在 SendText 中,首先都会根据短信地址和内容获取一个SmsMessage.SubmitPdu 对象(根据gsm 和 cdma获取不同的pdu)。然后再把目标地址、短信中心地址、短信内容、获取到的submitpdu对象打包成一个hashmap 对象 。然后把这个hashmap 对象、 发送报告和接收报告的pendingIntent和RadioTechnologyFamily(gsm是 RadioTechnologyFamily.RADIO_TECH_3GPP, cdma是 RadioTechnologyFamily.RADIO_TECH_3GPP2)。 最后,调用sendSubmitPdu(tracker)。 

  在sendSubmitPdu中, 会调用sendRawPdu(tracker); 在这个方法中,获取到封装在tracker中的pdu,sendintent等一些必要参数,然后判断phone 的状态,是否是ServiceState.STATE_IN_SERVICE,如果状态是activite的话,再检测Mms应用在一定时间内(默认一个小时)发送短信的条数是否超过最大限制(默认100条)。 如果超过限制,会弹出确认发送的对话框,此逻辑比较简单,在此不再多说; 若果没有超过最大发送条数限制,则把此信息添加到短信发送列表中,或者直接发送(没有短信在队列中等待发送)。

    sendSms(...)。调用 Ril.java 中的 sendCdmaSms\sendSms 。 在 ril.java 中 ,发送一个RIL_REQUEST_SEND_SMS消息,通知rild 去处理,rild 再发送请求给modem侧,由于没有看过modem的代码,具体的就不多说。


     ps:由于个人能力有限,都是根据个人平时工作时的积累和总结。如果有错误,欢迎大家指正,不胜感激 !

     http://blog.csdn.net/luzeqiang/article/details/7688407