ComposeMessageActivity.java
sendMessage
mWorkingMessage.setWorkingMessageSub(mSelectedSubId);设置subId
mWorkingMessage.send(mDebugRecipients);
我们注意,在输入收件人获取的收件人和当前的mConversation.getRecipients()是不一样的,我们并不会每次编辑一次收件人就将其设置mConversation中,而我们发送短信时,当是新建短信人,其收件人是从收件编辑框获取,而不是新建,则是从mConversation中获取。
WorkingMessage.java代表着是一个当前正在编辑的短信
Conversation.java代表着当前的会话(一个会话对应着和一个联系人的联系,包含着多条短信彩信)
mText里面放着是发送的文本
new Thread(new Runnable() {
@Override
public void run() {
preSendSmsWorker(conv, msgText, recipientsInUI);
updateSendStats(conv);
}
}, "WorkingMessage.send SMS").start();
启动后台线程来发送短信
preSendSmsWorker
mStatusListener.onPreMessageSent(); ComposeMessageActivity实现了这个借口,这样是根据短信当前状态来更新UI
发送之前对UI进行更新
ComposeMessageActivity
resetMessage() 有可能之前是新建短信,所以发送后要隐藏收件人编辑框等,还有处理焦点等
sendSmsWorker(msgText, semiSepRecipients, threadId);
调用SmsMessageSender的sendMessage来发送短信
deleteDraftSmsMessage(threadId);因为发送了,所以将属于这个thread对话的所有草稿都删除。
SmsMessageSender.java 其实这个类并不是执行真正发送短信的类,主要工作是新建sms记录,然后启动广播
sender = new SmsMessageSender(mActivity, dests, msgText, threadId,
mCurrentConvSubId);
先将发送短信需要的参数都传递进去
mStatusListener.onMessageSent();更新短信UI
SmsMessageSender.java
sendMessage
queueMessage
requestDeliveryReport是否需要发送报告,对应短信设置中的送达情况报告
int slotId = SubscriptionManager.getSlotId(mSubId);
这里对subId和slotId进行大致讲解:
subId,对应物理卡槽的位置,所以对于双卡,subId就分别为1和2,默认-1
slotId,是不断往上递增,也就是手机记录了手机插入的所有卡的信息,然后将他们存入到数据库中,可以看telephony表
Sms.addMessageToUri调用Telephony的方法在短信数据库的sms表中创建一条记录,thread表在之前获取threadId的时候就已经创建记录了。
Uri.parse("content://sms/queued")这是表示待发送的短信,一般uri后边附上queued这些类型的,都是设置字段type,表示当前短信的各种状态。
这里注意一点
if (deliveryReport) {
values.put(STATUS, STATUS_PENDING);
}即如果需要发送情况报告的话,要将表中的status设置为pending
Intent intent = new Intent(SmsReceiverService.ACTION_SEND_MESSAGE, null, mContext,
SmsReceiver.class);
intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, mSubId);
// Notify the SmsReceiverService to send the message out
mContext.sendBroadcast(intent);
接着启动广播
启动广播SmsReceiver.java
其action是SmsReceiverService中的
public static final String ACTION_SEND_MESSAGE =
"com.android.mms.transaction.SEND_MESSAGE";
onReceive
onReceiveWithPrivilege
intent.setClass(context, SmsReceiverService.class);
intent.putExtra("result", getResultCode());
beginStartingService(context, intent);
启动服务SmsReceiverService,因为intent是之前传递过来的,所以之前设置的一些内容,例如action都还在的
onStartCommand
ServiceHandler mServiceHandler
handleSendMessage(intent);
sendFirstQueuedMessage(subId)为什么是first?这是考虑到长短信的场景,一条长短信要被分割成多条短信进行发送,但是数据库中只有一条,显示也只显示一条
final Uri uri = Uri.parse("content://sms/queued");
ContentResolver resolver = getContentResolver();
String where = "sub_id=?";
String[] whereArgs = new String[] {Integer.toString(subscription)};
Cursor c = SqliteWrapper.query(this, resolver, uri, SEND_PROJECTION, where, whereArgs, "date ASC");
将数据库中,queue状态的,subId等于要发送的subId,都从数据库查找出来,这样群发的就会将所有要群发的都查找出来。
if (c.moveToNext()) {
isExpectMore = true; 所以这个表示了是否还有短信要发送,跟群发有关
}
SmsSingleRecipientSender这个类才是真正负责发送短信的类
调用sendMessage来发送短信
messageFailedToSend(msgUri, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
如果遇到异常,会将其type修改为failed
SmsManager smsManager = SmsManager.getSmsManagerForSubscriptionId(mSubId);
ArrayList<String> messages 长短信会被切割
messages = smsManager.divideMessage(mMessageText); 分割短信
boolean moved = Sms.moveMessageToFolder(mContext, mUri, Sms.MESSAGE_TYPE_OUTBOX, 0);
改变其字段type,将其移动到发件箱
ArrayList<PendingIntent> deliveryIntents = new ArrayList<PendingIntent>(messageCount);送达情况报告,这个是要送达到对方手机
ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>(messageCount);发送出去,只要发送成功就可以
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);
intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, mSubId);
sentIntents.add(PendingIntent.getBroadcast(mContext, requestCode, intent, 0));
smsManager.sendMultipartTextMessage(mDest, mServiceCenter, messages,
sentIntents, deliveryIntents, mPriority, isExpectMore, validityPeriod);
这个才是真正发送短信的,当前,这个是在应用层的最终地方,到了framework层还有其他流程。
前面sentIntents,当短信发送,无论成功还是失败,会调用这个Intent,所以SmsReceiver->SmsReceiverService还是这个流程。
if (MESSAGE_SENT_ACTION.equals(intent.getAction())) {
handleSmsSent(intent, error);
}
if (resultCode == Activity.RESULT_OK) 如果之前的发送成功,则
boolean sendNextMsg = intent.getBooleanExtra(EXTRA_MESSAGE_SENT_SEND_NEXT, false);
是否还有待发送的短信,记得之前isExpectMore ,应该就是和这个对应,有的话
if (sendNextMsg) {
Log.v(TAG, "handleSmsSent: move message to sent folder uri: " + uri);
if (!Sms.moveMessageToFolder(this, uri, Sms.MESSAGE_TYPE_SENT, error)) {
Log.e(TAG, "handleSmsSent: failed to move message " + uri + " to sent folder");
}
sendFirstQueuedMessage(subId);
}
else if ((resultCode == SmsManager.RESULT_ERROR_RADIO_OFF) ||
(resultCode == SmsManager.RESULT_ERROR_NO_SERVICE))
如果是因为这两种情况,一种是射频关闭,例如飞行模式(其中,如果处于飞行模式的时候,发送按钮是不可用的),一种是没有信号,则重新将他们放入到queue中
Sms.moveMessageToFolder(this, uri, Sms.MESSAGE_TYPE_QUEUED, error);
然后弹出目前无法发送信息,系统会在服务恢复后发送。提醒
这个系统服务恢复是否在下一次用同一个subId发送短信的时候才检测?看代码应该是。
else if (resultCode == SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE)
标志为错误,然后提醒,您只能向固定拨号号码发送信息。接着发送下一条短信如果有的话
最后剩下的,则归类于发送失败,然后接着发送下一条
messageFailedToSend(uri, error);
if (sendNextMsg) {
sendFirstQueuedMessage(subId);
}