簡訊傳送--簡訊傳送流程--應用層
阿新 • • 發佈:2019-01-05
簡訊傳送流程應用層解析
1、涉及的類
2、時序圖 說明:從ui介面開始,到呼叫中間層SmsManger的方法傳送簡訊,大致時序就是這樣,參考程式碼是android 2.3com.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
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開始到中間層執行傳送的操作,當然這裡還有很多不詳的地方,我也在嘗試不斷的完善。