1. 程式人生 > >簡訊傳送--簡訊傳送流程--應用層

簡訊傳送--簡訊傳送流程--應用層

簡訊傳送流程應用層解析

1、涉及的類

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
2、時序圖 說明:從ui介面開始,到呼叫中間層SmsManger的方法傳送簡訊,大致時序就是這樣,參考程式碼是android 2.3

3、流程解析

3.1 ComposeMessageActivity工作

該類是我們編輯簡訊的UI,與使用者互動,如下圖所示


當用戶編輯完成,即可點擊發送的按鈕,將簡訊內容傳送出去,點選sendbutton就會觸發該button對應的監聽器,由於ComposeMessageActivity實現了OnClickListener介面,所以最終呼叫到了onclick方法裡。

1)onClick分析

   該方法做了兩件件事情:

    一是呼叫isPreparedForSending方法判斷當前簡訊是否準備傳送,依據就是簡訊簡訊的接收者是否超過允許的上限,是否有接收者,以及簡訊是否有內容或者附件、主題之類的,不允許使用者傳送一條什麼都沒有的簡訊出去。

   二是,上面的檢查通過呼叫confirmSendMessageIfNeeded方法開始傳送流程。當然並不是呼叫了該方法就一定能傳送成功,該方法同樣會做一系列的檢查,直到符合要求了才會放行。

2)confirmSendMessageIfNeeded分析 

該方法的邏輯呼叫如下圖所示:

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()方法把處理髮送的任務丟擲去。

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()簡析: 1、判斷雙卡是否都可以使用,如果是獲取當前的卡並呼叫sendFirstQueuedMessage(int sub) 2、如果雙卡不是都可以使用,就直接呼叫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);
        }
4、總結

這部分主要是分析了短息的傳送的一個流程,從ui點選button開始到中間層執行傳送的操作,當然這裡還有很多不詳的地方,我也在嘗試不斷的完善。