短信發送流程應用層解析
一、涉及的類
- com.android.mms.ui.ComposeMessageActivity
- com.android.mms.data.WorkingMessage
- com.android.mms.transaction.MessageSender
- com.android.mms.transaction.SmsMessageSender
- com.android.mms.transaction.SmsSingleRecipientSender
- com.android.mms.transaction.SmsReceiverService
- com.android.mms.transaction.SmsReceiver
二、時序圖
說明:從ui界面開始,到調用中間層SmsManger的方法發送短信,大體時序就是這樣,參考代碼是android 2.3
三、流程解析
3.1 ComposeMessageActivity工做
該類是咱們編輯短信的UI,與用戶交互,以下圖所示 android

當用戶編輯完成,便可點擊發送的按鈕,將短信內容發送出去,點擊sendbutton就會觸發該button對應的監聽器,因爲ComposeMessageActivity實現了OnClickListener接口,因此最終調用到了onclick方法裏。 數據庫
1)onClick分析 app
該方法作了兩件件事情: ide
一是調用isPreparedForSending方法判斷當前短信是否準備發送,依據就是短信短信的接收者是否超過容許的上限,是否有接收者,以及短信是否有內容或者附件、主題之類的,不容許用戶發送一條什麼都沒有的短信出去。 函數
二是,上面的檢查經過調用confirmSendMessageIfNeeded方法開始發送流程。固然並非調用了該方法就必定能發送成功,該方法一樣會作一系列的檢查,直到符合要求了纔會放行。 ui
2)confirmSendMessageIfNeeded分析 this
該方法的邏輯調用以下圖所示: spa
3)sendMessage方法分析
上圖能夠看出最後都要走到sendMessage來,咱們來看看這個方法到底作了哪些工做。
經過查看代碼咱們能夠發現最最核心的工做就是: 把發送短信的工做交給WorkingMessage,mWorkingMessage.send(mDebugRecipients);其餘的工做也僅僅是作一些輔助型的操做。
小結:到此爲止發送短信的工做交給了WorkingMessage,那
ComposeMessageActivity主要的工做便是對雙卡的處理。
3.2 WorkingMessage簡單分析
1)send()分析
該方法作了五項工做:
一是 檢查接收者列表時否爲空,這裏我就不作具體的分析。
二是將短信內容從8字節轉換成7字節;
三是判斷當前是不是發送彩信,咱們當前是短信發送,因此可定不會走彩信的發送流程。
四是,將短信的簽名加到短信的內容上。
五是調用preSendSmsWorker()方法。
2)preSendSmsWorker分析
一是重置界面,將界面上的各個組件所有清除
二是調用sendSmsWorker方法
三是刪除草稿。
3)sendSmsWorker()所作的工做
調用SmsMessageSender的sendMessage()方法
3.3 SmsMessageSender簡析
1)sendMessage()
該方法會調用queueMessage()方法把處理髮送的任務拋出去。 .net
2)queueMessage()
它的職責有兩個:
一是將要發送的短息保存到數據庫;
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
- boolean requestDeliveryReport = prefs.getBoolean(
- MessagingPreferenceActivity.SMS_DELIVERY_REPORT_MODE,
- DEFAULT_DELIVERY_REPORT_MODE);
-
- for (int i = 0; i < mNumberOfDests; i++) {
- try {
- log("updating Database with sub = " + mSubscription);
- Sms.addMessageToUri(mContext.getContentResolver(),
- Uri.parse("content://sms/queued"), mDests[i],
- mMessageText, null, mTimestamp,
- true /* read */,
- requestDeliveryReport,
- mThreadId, mSubscription);
- } catch (SQLiteException e) {
- SqliteWrapper.checkSQLiteException(mContext, e);
- }
- }
二是,將任務轉交到其餘人,只不過它採用的方式是發廣播;
- // Notify the SmsReceiverService to send the message out
- Intent intent = new Intent(SmsReceiverService.ACTION_SEND_MESSAGE, null, mContext, SmsReceiver.class);
- intent.putExtra(SUBSCRIPTION, mSubscription);
- mContext.sendBroadcast(intent);
小結:該類作了一個很重要的工做就是講要發送的短信保存進入數據庫,而後發廣播通知SmsReceiver;
3.4 SmsReceiver 到 SmsReceiverService 簡析
實際上SmsReceiver這傢伙也不是幹事的人,它僅僅是拿到手裏後立刻就轉交給SmsReceiverService服務了,「這事不歸我管,我就是一個送快遞的「,SmsReceiver的角色就是這樣的,調用的方法能夠參考時序圖;
3.5 SmsReceiverService 簡析
講了好久終於幹活的來了,它既然是一個服務,固然它會走本身的聲明周期函數,首先是onCrate,該方法近幾年是初始化,而後是onStartCommand(),該方法也沒作啥,僅僅是向ServiceHandler發送消息,看來人家作苦力都作出心得了。
1)ServiceHandler處理髮送請求
- @Override
- public void handleMessage(Message msg) {
- int serviceId = msg.arg1;
- Intent intent = (Intent)msg.obj;
- if (intent != null) {
- String action = intent.getAction();
-
- int error = intent.getIntExtra("errorCode", 0);
- if (SMS_RECEIVED_ACTION.equals(action)) {
- handleSmsReceived(intent, error);
- } else if (SMS_CB_RECEIVED_ACTION.equals(action)) {
- handleCbSmsReceived(intent, error);
- } else if (ACTION_BOOT_COMPLETED.equals(action)) {
- handleBootCompleted();
- } else if (TelephonyIntents.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
- handleServiceStateChanged(intent);
- } else if (ACTION_SEND_MESSAGE.endsWith(action)) {
- handleSendMessage(intent);
- }
- }
- // NOTE: We MUST not call stopSelf() directly, since we need to
- // make sure the wake lock acquired by AlertReceiver is released.
- SmsReceiver.finishStartingService(SmsReceiverService.this, serviceId);
- }
- }
這裏接收到發送後會走handleSendMessage方法;
2)handleSendMessage()簡析:
一、判斷雙卡是否均可以使用,若是是獲取當前的卡並調用sendFirstQueuedMessage(int sub)
二、若是雙卡不是均可以使用,就直接調用sendFirstQueuedMessage()方法;
注意:這裏是調用的兩個不一樣的方法,看他們的參數你就知道了,但實際上sendFirstQueuedMessage()無參的函數最終仍是經過調用sendFirstQueuedMessage(int sub)來實現的;至關於最後仍是調用的sendFirstQueuedMessage(int sub)這個方法;
3)sendFirstQueuedMessage(int sub)簡析
它首先是從數據庫中取出短信,而後調用SmsSingleRecipientSender的sendMessage()方法發送;
小結:你們能夠發現走了半天,最後仍是沒有開始發送。
3.6 SmsSingleRecipientSender簡析
sendMessage()說明:
一是對短信的內容進行分割
二是將短信保存到OUTBOX的數據庫表裏
三是將分割的短信分開發送
四是調用的SMSManger類的sendMultipartTextMessage()發送,將發送的具體操做轉移給中間層。
具體代碼以下:
- if (mMessageText == null) {
- // Don't try to send an empty message, and destination should be just
- // one.
- throw new MmsException("Null message body or have multiple destinations.");
- }
- SmsManager smsManager = SmsManager.getDefault();
- ArrayList<String> messages = null;
- if ((MmsConfig.getEmailGateway() != null) &&
- (Mms.isEmailAddress(mDest) || MessageUtils.isAlias(mDest))) {
- String msgText;
- msgText = mDest + " " + mMessageText;
- mDest = MmsConfig.getEmailGateway();
- messages = smsManager.divideMessage(msgText);
- } else {
- messages = smsManager.divideMessage(mMessageText);
- // remove spaces from destination number (e.g. "801 555 1212" -> "8015551212")
- mDest = mDest.replaceAll(" ", "");
- }
- int messageCount = messages.size();
-
- if (messageCount == 0) {
- // Don't try to send an empty message.
- throw new MmsException("SmsMessageSender.sendMessage: divideMessage returned " +
- "empty messages. Original message is \"" + mMessageText + "\"");
- }
-
- boolean moved = Sms.moveMessageToFolder(mContext, mUri, Sms.MESSAGE_TYPE_OUTBOX, 0);
- if (!moved) {
- throw new MmsException("SmsMessageSender.sendMessage: couldn't move message " +
- "to outbox: " + mUri);
- }
-
- ArrayList<PendingIntent> deliveryIntents = new ArrayList<PendingIntent>(messageCount);
- ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>(messageCount);
- for (int i = 0; i < messageCount; i++) {
- if (mRequestDeliveryReport) {
- // TODO: Fix: It should not be necessary to
- // specify the class in this intent. Doing that
- // unnecessarily limits customizability.
- deliveryIntents.add(PendingIntent.getBroadcast(
- mContext, 0,
- new Intent(
- MessageStatusReceiver.MESSAGE_STATUS_RECEIVED_ACTION,
- mUri,
- mContext,
- MessageStatusReceiver.class),
- 0));
- }
- Intent intent = new Intent(SmsReceiverService.MESSAGE_SENT_ACTION,
- mUri,
- mContext,
- SmsReceiver.class);
-
- int requestCode = 0;
- if (i == messageCount -1) {
- // Changing the requestCode so that a different pending intent
- // is created for the last fragment with
- // EXTRA_MESSAGE_SENT_SEND_NEXT set to true.
- requestCode = 1;
- intent.putExtra(SmsReceiverService.EXTRA_MESSAGE_SENT_SEND_NEXT, true);
- intent.putExtra(SUBSCRIPTION, mSubscription);
- }
- sentIntents.add(PendingIntent.getBroadcast(mContext, requestCode, intent, 0));
- }
- try {
- smsManager.sendMultipartTextMessage(mDest, mServiceCenter, messages, sentIntents,
- deliveryIntents, mSubscription);
- } catch (Exception ex) {
- throw new MmsException("SmsMessageSender.sendMessage: caught " + ex +
- " from SmsManager.sendTextMessage()");
- }
- if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
- log("sendMessage: address=" + mDest + ", threadId=" + mThreadId +
- ", uri=" + mUri + ", msgs.count=" + messageCount);
- }