转载请注明出处:http://blog.csdn.net/droyon/article/details/10194591
源码版本来自:Android4.0.3
忙了两三个月,终于有时间可以写点东西了。
有人问我怎么了解原生应用的流程,可能每个人有最适合每个人的方法,现将我的方法说一下,供大家参考:
1、通过DEBUG,DEBUG可以了解每一步代码做了什么,而且可以看到堆栈执行过程。
2、通过打Log的方式。在自己不了解的流程模块打大量的log信息,根据log信息,摸清“模块”的底细。
上述两种方式都需要足够的耐心,但是逐渐接触谜团的真相的过程,确实很享受。好奇心是程序猿,攻城狮的朋友。
相关文章:
---------------------------------------------------------------
2、短信发送framework层逻辑
3、短信发送pdu包压缩解析
---------------------------------------------------------------
现将短信发送的流程整理一下,不当之处请大家斧正。
短信发送大体可以分为下面三个阶段:
我们现在详细叙述一下:
1、短信发送上层逻辑流程
这个界面叫做“会话信息列表界面”,这个界面保存了短彩信所有的会话条目
(会话:英文名Thread,一条会话会记录当前会话内的短信往来信息,例如图片内的10086会话)
点击左下角的新建按钮,那么我们就会进入下面的这个界面:
这个界面叫做“信息列表界面”,它记录了会话内短信往来的所有条目。信息条目按照时间倒序排列:最新的信息条目显示在列表最下方。
我们的短信流程就从这个界面开始,当我们输入完要编辑的信息,点击右下角的发送按钮。
类名:ComposeMessageActivity.java
点击事件:
@Override public void onClick(View v) { if ((v == mSendButtonSms || v == mSendButtonMms) && isPreparedForSending()) { confirmSendMessageIfNeeded(); } else if ((v == mRecipientsPicker)) { launchMultiplePhonePicker(); } }方法堆栈进入confirmSendMessageIfNeeded,让我们看看confirmSendMessageIfNeeded()方法:
private void confirmSendMessageIfNeeded() { if (!isRecipientsEditorVisible()) { sendMessage(true); return; } boolean isMms = mWorkingMessage.requiresMms(); if (mRecipientsEditor.hasInvalidRecipient(isMms)) { if (mRecipientsEditor.hasValidRecipient(isMms)) { String title = getResourcesString(R.string.has_invalid_recipient, mRecipientsEditor.formatInvalidNumbers(isMms)); new AlertDialog.Builder(this) .setTitle(title) .setMessage(R.string.invalid_recipient_message) .setPositiveButton(R.string.try_to_send, new SendIgnoreInvalidRecipientListener()) .setNegativeButton(R.string.no, new CancelSendingListener()) .show(); } else { new AlertDialog.Builder(this) .setTitle(R.string.cannot_send_message) .setMessage(R.string.cannot_send_message_reason) .setPositiveButton(R.string.yes, new CancelSendingListener()) .show(); } } else { // The recipients editor is still open. Make sure we use what's showing there // as the destination. ContactList contacts = mRecipientsEditor.constructContactsFromInput(false); mDebugRecipients = contacts.serialize(); sendMessage(true); } }
首先判断联系人输入框是否可见,如果不可见,那么可以直接发送。
然后检查是否有非法的联系人,如果存在,弹出AlertDialog给用户提示。
如果没有非法的联系人,那么直接发送。方法堆栈进入sendMessage(true)
让我们看看sendMessage(true)的逻辑
private void sendMessage(boolean bCheckEcmMode) { if (bCheckEcmMode) { // TODO: expose this in telephony layer for SDK build String inEcm = SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE); if (Boolean.parseBoolean(inEcm)) { try { startActivityForResult( new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null), REQUEST_CODE_ECM_EXIT_DIALOG); return; } catch (ActivityNotFoundException e) { // continue to send message Log.e(TAG, "Cannot find EmergencyCallbackModeExitDialog", e); } } } if (!mSendingMessage) { if (LogTag.SEVERE_WARNING) { String sendingRecipients = mConversation.getRecipients().serialize(); if (!sendingRecipients.equals(mDebugRecipients)) { String workingRecipients = mWorkingMessage.getWorkingRecipients(); if (!mDebugRecipients.equals(workingRecipients)) { LogTag.warnPossibleRecipientMismatch("ComposeMessageActivity.sendMessage" + " recipients in window: \"" + mDebugRecipients + "\" differ from recipients from conv: \"" + sendingRecipients + "\" and working recipients: " + workingRecipients, this); } } sanityCheckConversation(); } // send can change the recipients. Make sure we remove the listeners first and then add // them back once the recipient list has settled. removeRecipientsListeners(); mWorkingMessage.send(mDebugRecipients); mSentMessage = true; mSendingMessage = true; addRecipientsListeners(); mScrollOnSend = true; // in the next onQueryComplete, scroll the list to the end. } // But bail out if we are supposed to exit after the message is sent. if (mSendDiscreetMode) { finish(); } }传进来的参数为true,也就是检查是否处于紧急模式,如果处于紧急模式,那么进行相关逻辑的处理,并退出发送。
如果当前非处于紧急模式,那么检查当前是否有正在发送的短信,那么在判断mSendDiscreetMode是否为true,如果为true,那么退出此界面。
如果当前不存在正在发送的短信,那么执行 mWorkingMessage.send(mDebugRecipients)。
让我们进入mWorkingMessage.send()方法:
类名:WorkingMessage.java
public void send(final String recipientsInUI) { long origThreadId = mConversation.getThreadId(); if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) { LogTag.debug("send origThreadId: " + origThreadId); } removeSubjectIfEmpty(true /* notify */); // Get ready to write to disk. prepareForSave(true /* notify */); // We need the recipient list for both SMS and MMS. final Conversation conv = mConversation; String msgTxt = mText.toString();//得到发送的内容 if (requiresMms() || addressContainsEmailToMms(conv, msgTxt)) {//检查当前信息的mMmsState以及是否含有email地址,来判断当前信息是否会转化为彩信 // uaProfUrl setting in mms_config.xml must be present to send an MMS. // However, SMS service will still work in the absence of a uaProfUrl address. if (MmsConfig.getUaProfUrl() == null) { String err = "WorkingMessage.send MMS sending failure. mms_config.xml is " + "missing uaProfUrl setting. uaProfUrl is required for MMS service, " + "but can be absent for SMS."; RuntimeException ex = new NullPointerException(err); Log.e(TAG, err, ex); // now, let's just crash. throw ex; } // Make local copies of the bits we need for sending a message, // because we will be doing it off of the main thread, which will // immediately continue on to resetting some of this state. final Uri mmsUri = mMessageUri; final PduPersister persister = PduPersister.getPduPersister(mActivity); final SlideshowModel slideshow = mSlideshow; final CharSequence subject = mSubject; final boolean textOnly = mAttachmentType == TEXT; if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) { LogTag.debug("Send mmsUri: " + mmsUri); } // Do the dirty work of sending the message off of the main UI thread. new Thread(new Runnable() { @Override public void run() { final SendReq sendReq = makeSendReq(conv, subject); // Make sure the text in slide 0 is no longer holding onto a reference to // the text in the message text box. slideshow.prepareForSend(); sendMmsWorker(conv, mmsUri, persister, slideshow, sendReq, textOnly); updateSendStats(conv); } }, "WorkingMessage.send MMS").start(); } else {//上面是彩信的逻辑,我们暂且跳过这块,下面的内容,是处理短信的逻辑 // Same rules apply as above. final String msgText = mText.toString(); new Thread(new Runnable() { @Override public void run() { preSendSmsWorker(conv, msgText, recipientsInUI); updateSendStats(conv); } }, "WorkingMessage.send SMS").start(); } // update the Recipient cache with the new to address, if it's different RecipientIdCache.updateNumbers(conv.getThreadId(), conv.getRecipients()); // Mark the message as discarded because it is "off the market" after being sent. mDiscarded = true; }
我们主要看一下短信的逻辑部分:
开启一个线程,主线程返回,在新开启的线程里会执行 preSendSmsWorker()以及updateSendState()这两个方法。让我们看看这两个方法的内容:
preSendSmsWorker:
private void preSendSmsWorker(Conversation conv, String msgText, String recipientsInUI) { // If user tries to send the message, it's a signal the inputted text is what they wanted. UserHappinessSignals.userAcceptedImeText(mActivity); mStatusListener.onPreMessageSent(); long origThreadId = conv.getThreadId(); // Make sure we are still using the correct thread ID for our recipient set. long threadId = conv.ensureThreadId(); String semiSepRecipients = conv.getRecipients().serialize(); // recipientsInUI can be empty when the user types in a number and hits send if (LogTag.SEVERE_WARNING && ((origThreadId != 0 && origThreadId != threadId) || (!semiSepRecipients.equals(recipientsInUI) && !TextUtils.isEmpty(recipientsInUI)))) { String msg = origThreadId != 0 && origThreadId != threadId ? "WorkingMessage.preSendSmsWorker threadId changed or " + "recipients changed. origThreadId: " + origThreadId + " new threadId: " + threadId + " also mConversation.getThreadId(): " + mConversation.getThreadId() : "Recipients in window: \"" + recipientsInUI + "\" differ from recipients from conv: \"" + semiSepRecipients + "\""; // Just interrupt the process of sending message if recipient mismatch LogTag.warnPossibleRecipientMismatch(msg, mActivity); }else { // just do a regular send. We're already on a non-ui thread so no need to fire // off another thread to do this work. sendSmsWorker(msgText, semiSepRecipients, threadId); // Be paranoid and clean any draft SMS up. deleteDraftSmsMessage(threadId); } }
在这个方法中,首先 检查/创建 一个threadId,并且调用sendSmsWorker方法。最后删除当前会话内的草稿短信。
比较重要的是两个方法,第一个是conv.ensureThreadId,另一个是sendSmsWorker,我们先来看一下conv.ensureThreadId这个方法:
Conversation.java//保存了和会话信息有关的信息
public synchronized long ensureThreadId() { if (DEBUG || DELETEDEBUG) { LogTag.debug("ensureThreadId before: " + mThreadId); } if (mThreadId <= 0) { mThreadId = getOrCreateThreadId(mContext, mRecipients); } if (DEBUG || DELETEDEBUG) { LogTag.debug("ensureThreadId after: " + mThreadId); } return mThreadId; }
注意这个方法加了同步锁(synchronized),在这个方法中,我们看到,如果mThreadId不小于0,那么返回mThreadId,如果mThreadId小于0,那么,执行getOrCreateThreadId方法。
这个方法有两个参数,第一个参数为Context,重要的是第二个参数,为当前会话的联系人集合(保存了当前要发送的所有联系人)
private static long getOrCreateThreadId(Context context, ContactList list) { HashSet<String> recipients = new HashSet<String>(); Contact cacheContact = null; for (Contact c : list) { cacheContact = Contact.get(c.getNumber(), false); if (cacheContact != null) { recipients.add(cacheContact.getNumber()); } else { recipients.add(c.getNumber()); } }//将联系人的信息导入到recipients中 synchronized(sDeletingThreadsLock) {//加锁,同步代码快 if (DELETEDEBUG) { ComposeMessageActivity.log("Conversation getOrCreateThreadId for: " + list.formatNamesAndNumbers(",") + " sDeletingThreads: " + sDeletingThreads); } long now = System.currentTimeMillis(); while (sDeletingThreads) { try { sDeletingThreadsLock.wait(30000); } catch (InterruptedException e) { } if (System.currentTimeMillis() - now > 29000) { // The deleting thread task is stuck or onDeleteComplete wasn't called. // Unjam ourselves. Log.e(TAG, "getOrCreateThreadId timed out waiting for delete to complete", new Exception()); sDeletingThreads = false; break; } } long retVal = Threads.getOrCreateThreadId(context, recipients);//重要的是这个方法 if (DELETEDEBUG || Log.isLoggable(LogTag.APP, Log.VERBOSE)) { LogTag.debug("[Conversation] getOrCreateThreadId for (%s) returned %d", recipients, retVal); } return retVal; } }
在这个方法中,我们通过执行Threads.getOrCreateTheadId方法来得到或者创建一个ThreadId,并返回。
我们进到Threads.getOrCreateTheadId:
Telephony.java
public static long getOrCreateThreadId( Context context, Set<String> recipients) { Uri.Builder uriBuilder = THREAD_ID_CONTENT_URI.buildUpon(); for (String recipient : recipients) { if (Mms.isEmailAddress(recipient)) { recipient = Mms.extractAddrSpec(recipient); } uriBuilder.appendQueryParameter("recipient", recipient); } Uri uri = uriBuilder.build(); //if (DEBUG) Rlog.v(TAG, "getOrCreateThreadId uri: " + uri); Cursor cursor = SqliteWrapper.query(context, context.getContentResolver(), uri, ID_PROJECTION, null, null, null); if (cursor != null) { try { if (cursor.moveToFirst()) { return cursor.getLong(0); } else { Rlog.e(TAG, "getOrCreateThreadId returned no rows!"); } } finally { cursor.close(); } } Rlog.e(TAG, "getOrCreateThreadId failed with uri " + uri.toString()); throw new IllegalArgumentException("Unable to find or allocate a thread ID."); } }我们在这个方法汇总只看到了query,并没有看到insert,那么ThreadId在哪里创建的那?
MmsSmsProvider.javaquery方法:
case URI_THREAD_ID: List<String> recipients = uri.getQueryParameters("recipient"); cursor = getThreadId(recipients); break;
private synchronized Cursor getThreadId(List<String> recipients) { Set<Long> addressIds = getAddressIds(recipients); String recipientIds = ""; if (addressIds.size() == 0) { Log.e(LOG_TAG, "getThreadId: NO receipients specified -- NOT creating thread", new Exception()); return null; } else if (addressIds.size() == 1) { // optimize for size==1, which should be most of the cases for (Long addressId : addressIds) { recipientIds = Long.toString(addressId); } } else { recipientIds = getSpaceSeparatedNumbers(getSortedSet(addressIds)); } if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) { Log.d(LOG_TAG, "getThreadId: recipientIds (selectionArgs) =" + /*recipientIds*/ "xxxxxxx"); } String[] selectionArgs = new String[] { recipientIds }; SQLiteDatabase db = mOpenHelper.getReadableDatabase(); db.beginTransaction(); Cursor cursor = null; try { // Find the thread with the given recipients cursor = db.rawQuery(THREAD_QUERY, selectionArgs); if (cursor.getCount() == 0) { // No thread with those recipients exists, so create the thread. cursor.close(); Log.d(LOG_TAG, "getThreadId: create new thread_id for recipients " + /*recipients*/ "xxxxxxxx"); insertThread(recipientIds, recipients.size()); // The thread was just created, now find it and return it. cursor = db.rawQuery(THREAD_QUERY, selectionArgs); } db.setTransactionSuccessful(); } catch (Throwable ex) { Log.e(LOG_TAG, ex.getMessage(), ex); } finally { db.endTransaction(); } if (cursor != null && cursor.getCount() > 1) { Log.w(LOG_TAG, "getThreadId: why is cursorCount=" + cursor.getCount()); } return cursor; }
首先:
"SELECT _id FROM threads " + "WHERE recipient_ids=?";如果查询结果数目为0,也就是说当前联系人不存在任何一个会话(Thread)中,那么我们就执行insertThread创建一个会话。
关于insertThread方法:
private void insertThread(String recipientIds, int numberOfRecipients) { ContentValues values = new ContentValues(4); long date = System.currentTimeMillis(); values.put(ThreadsColumns.DATE, date - date % 1000); values.put(ThreadsColumns.RECIPIENT_IDS, recipientIds); if (numberOfRecipients > 1) { values.put(Threads.TYPE, Threads.BROADCAST_THREAD); } values.put(ThreadsColumns.MESSAGE_COUNT, 0); long result = mOpenHelper.getWritableDatabase().insert(TABLE_THREADS, null, values); Log.d(LOG_TAG, "insertThread: created new thread_id " + result + " for recipientIds " + /*recipientIds*/ "xxxxxxx"); getContext().getContentResolver().notifyChange(MmsSms.CONTENT_URI, null); }
WorkingMessage.java
在之前,我们得到了mThreadId,那么我们就可以继续往下走了
sendSmsWorker(msgText, semiSepRecipients, threadId); // Be paranoid and clean any draft SMS up. deleteDraftSmsMessage(threadId);
我们首先来看看,sendSmsWorker方法:
private void sendSmsWorker(String msgText, String semiSepRecipients, long threadId) { String[] dests = TextUtils.split(semiSepRecipients, ";"); if (LogTag.VERBOSE || Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) { Log.d(LogTag.TRANSACTION, "sendSmsWorker sending message: recipients=" + semiSepRecipients + ", threadId=" + threadId); } MessageSender sender = new SmsMessageSender(mActivity, dests, msgText, threadId); try { sender.sendMessage(threadId); // Make sure this thread isn't over the limits in message count Recycler.getSmsRecycler().deleteOldMessagesByThreadId(mActivity, threadId); } catch (Exception e) { Log.e(TAG, "Failed to send SMS message, threadId=" + threadId, e); } mStatusListener.onMessageSent(); MmsWidgetProvider.notifyDatasetChanged(mActivity); }
mStatusListener.onMessageSent()是个回调,告诉ComposeMessageActivity发送完成。
我们主要来看看sendMessage方法。
SmsMessageSender.java
public boolean sendMessage(long token) throws MmsException { // In order to send the message one by one, instead of sending now, the message will split, // and be put into the queue along with each destinations return queueMessage(token); }
queueMessage:
private boolean queueMessage(long token) throws MmsException { if ((mMessageText == null) || (mNumberOfDests == 0)) { // Don't try to send an empty message. throw new MmsException("Null message body or dest."); } 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 { if (LogTag.DEBUG_SEND) { Log.v(TAG, "queueMessage mDests[i]: " + mDests[i] + " mThreadId: " + mThreadId); } Sms.addMessageToUri(mContext.getContentResolver(), Uri.parse("content://sms/queued"), mDests[i], mMessageText, null, mTimestamp, true /* read */, requestDeliveryReport, mThreadId); } catch (SQLiteException e) { if (LogTag.DEBUG_SEND) { Log.e(TAG, "queueMessage SQLiteException", e); } SqliteWrapper.checkSQLiteException(mContext, e); } } // Notify the SmsReceiverService to send the message out mContext.sendBroadcast(new Intent(SmsReceiverService.ACTION_SEND_MESSAGE, null, mContext, SmsReceiver.class)); return false; }
检查设置,是否需要发送报告。然后根据mNumberOfDests,执行Sms.addMessageToUri,将信息插入到数据库中。
我们看看插入过程:
Telephony.java
public static Uri addMessageToUri(ContentResolver resolver, Uri uri, String address, String body, String subject, Long date, boolean read, boolean deliveryReport, long threadId) { ContentValues values = new ContentValues(7); values.put(ADDRESS, address); if (date != null) { values.put(DATE, date); } values.put(READ, read ? Integer.valueOf(1) : Integer.valueOf(0)); values.put(SUBJECT, subject); values.put(BODY, body); if (deliveryReport) { values.put(STATUS, STATUS_PENDING); } if (threadId != -1L) { values.put(THREAD_ID, threadId); } return resolver.insert(uri, values); }
sms表上有若干触发器,此处会触发这个触发器:
CREATE TRIGGER sms_update_thread_on_insert AFTER INSERT ON sms BEGIN UPDATE threads SET date = (strftime('%s','now') * 1000), snippet = new.body, snippet_cs = 0 WHERE threads._id = new.thread_id; UPDATE threads SET message_count = (SELECT COUNT(sms._id) FROM sms LEFT JOIN threads ON threads._id = thread_id WHERE thread_id = new.thread_id AND sms.type != 3) + (SELECT COUNT(pdu._id) FROM pdu LEFT JOIN threads ON threads._id = thread_id WHERE thread_id = new.thread_id AND (m_type=132 OR m_type=130 OR m_type=128) AND msg_box != 3) WHERE threads._id = new.thread_id; UPDATE threads SET read = CASE (SELECT COUNT(*) FROM sms WHERE read = 0 AND thread_id = threads._id) WHEN 0 THEN 1 ELSE 0 END WHERE threads._id = new.thread_id; END
意思是在向sms数据表内插入数据后,会更新thread数据表。大家感兴趣的可以自己看一下,大体更新thread表的message_count等。
回到刚才那里,将信息插入到数据库之后,就发起一个广播,然后整个方法堆栈就结束了。
广播发送代码:
mContext.sendBroadcast(new Intent(SmsReceiverService.ACTION_SEND_MESSAGE, null, mContext, SmsReceiver.class));
SmsReceiverService.ACTION_SEND_MESSAGE
我们看看广播中的处理逻辑:
SmsReceiver.java
@Override public void onReceive(Context context, Intent intent) { onReceiveWithPrivilege(context, intent, false); }onReceiveWithPrivilege:
protected void onReceiveWithPrivilege(Context context, Intent intent, boolean privileged) { // If 'privileged' is false, it means that the intent was delivered to the base // no-permissions receiver class. If we get an SMS_RECEIVED message that way, it // means someone has tried to spoof the message by delivering it outside the normal // permission-checked route, so we just ignore it. if (!privileged && intent.getAction().equals(Intents.SMS_RECEIVED_ACTION)) {//接受信息的action return; } intent.setClass(context, SmsReceiverService.class); intent.putExtra("result", getResultCode()); beginStartingService(context, intent);//开启service }
beginStartingService:
public static void beginStartingService(Context context, Intent intent) { synchronized (mStartingServiceSync) { if (mStartingService == null) { PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); mStartingService = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "StartingAlertService"); mStartingService.setReferenceCounted(false); } mStartingService.acquire(); context.startService(intent);//开启service } }
综合来说,这段代码的作用就是在广播中开启了一个service。
我们看看service中的逻辑
smsReceiverService:
public void onCreate() { // Temporarily removed for this duplicate message track down. // if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE) || LogTag.DEBUG_SEND) { // Log.v(TAG, "onCreate"); // } // Start up the thread running the service. Note that we create a // separate thread because the service normally runs in the process's // main thread, which we don't want to block. HandlerThread thread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND); thread.start(); mServiceLooper = thread.getLooper(); mServiceHandler = new ServiceHandler(mServiceLooper); }
public int onStartCommand(Intent intent, int flags, int startId) { // Temporarily removed for this duplicate message track down. mResultCode = intent != null ? intent.getIntExtra("result", 0) : 0; if (mResultCode != 0) { Log.v(TAG, "onStart: #" + startId + " mResultCode: " + mResultCode + " = " + translateResultCode(mResultCode)); } Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; msg.obj = intent; mServiceHandler.sendMessage(msg); return Service.START_NOT_STICKY; }
public void handleMessage(Message msg) { int serviceId = msg.arg1; Intent intent = (Intent)msg.obj; if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) { Log.v(TAG, "handleMessage serviceId: " + serviceId + " intent: " + intent); } if (intent != null) { String action = intent.getAction(); int error = intent.getIntExtra("errorCode", 0); if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) { Log.v(TAG, "handleMessage action: " + action + " error: " + error); } if (MESSAGE_SENT_ACTION.equals(intent.getAction())) { handleSmsSent(intent, error); } else if (SMS_RECEIVED_ACTION.equals(action)) { handleSmsReceived(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(); } else if (ACTION_SEND_INACTIVE_MESSAGE.equals(action)) { handleSendInactiveMessage(); } } // 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); }
我们的action:SmsReceiverService.ACTION_SEND_MESSAGE,那么接下来执行:
handleSendMessage()
private void handleSendMessage() { if (!mSending) { sendFirstQueuedMessage(); } }
sendFirstQueueMessage:
public synchronized void sendFirstQueuedMessage() { boolean success = true; // get all the queued messages from the database final Uri uri = Uri.parse("content://sms/queued"); ContentResolver resolver = getContentResolver(); Cursor c = SqliteWrapper.query(this, resolver, uri, SEND_PROJECTION, null, null, "date ASC"); // date ASC so we send out in // same order the user tried // to send messages. if (c != null) { try { if (c.moveToFirst()) { String msgText = c.getString(SEND_COLUMN_BODY); String address = c.getString(SEND_COLUMN_ADDRESS); int threadId = c.getInt(SEND_COLUMN_THREAD_ID); int status = c.getInt(SEND_COLUMN_STATUS); int msgId = c.getInt(SEND_COLUMN_ID); Uri msgUri = ContentUris.withAppendedId(Sms.CONTENT_URI, msgId); SmsMessageSender sender = new SmsSingleRecipientSender(this, address, msgText, threadId, status == Sms.STATUS_PENDING, msgUri); if (LogTag.DEBUG_SEND || LogTag.VERBOSE || Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) { Log.v(TAG, "sendFirstQueuedMessage " + msgUri + ", address: " + address + ", threadId: " + threadId); } try { sender.sendMessage(SendingProgressTokenManager.NO_TOKEN);; mSending = true; } catch (MmsException e) { Log.e(TAG, "sendFirstQueuedMessage: failed to send message " + msgUri + ", caught ", e); mSending = false; messageFailedToSend(msgUri, SmsManager.RESULT_ERROR_GENERIC_FAILURE); success = false; // Sending current message fails. Try to send more pending messages // if there is any. sendBroadcast(new Intent(SmsReceiverService.ACTION_SEND_MESSAGE, null, this, SmsReceiver.class)); } } } finally { c.close(); } } if (success) { // We successfully sent all the messages in the queue. We don't need to // be notified of any service changes any longer. unRegisterForServiceStateChanges(); } }
在发送过程中,new一个SmsSingleRecipientSender,将address,msgTest,threadId,是否需要发送报告 ,等作为参数。然后执行此对象的sendMessage方法。
在sendMessage方法中:
public SmsSingleRecipientSender(Context context, String dest, String msgText, long threadId, boolean requestDeliveryReport, Uri uri) { super(context, null, msgText, threadId); mRequestDeliveryReport = requestDeliveryReport; mDest = dest; mUri = uri; } public boolean sendMessage(long token) throws MmsException { if (LogTag.DEBUG_SEND) { Log.v(TAG, "sendMessage token: " + token); } 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();//得到SmsMessager对象 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 and dashes from destination number // (e.g. "801 555 1212" -> "8015551212") // (e.g. "+8211-123-4567" -> "+82111234567") mDest = PhoneNumberUtils.stripSeparators(mDest); mDest = Conversation.verifySingleRecipient(mContext, mThreadId, mDest); } 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); } if (LogTag.DEBUG_SEND) { Log.v(TAG, "sendMessage mDest: " + mDest + " mRequestDeliveryReport: " + mRequestDeliveryReport); } ArrayList<PendingIntent> deliveryIntents = new ArrayList<PendingIntent>(messageCount);//构建两个peddingIntent,用于在信息发送完成之后,回调发送广播。或者在接收到短信状态报告之后回调发送广播 ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>(messageCount); for (int i = 0; i < messageCount; i++) {//遍历发送信息 if (mRequestDeliveryReport && (i == (messageCount - 1))) { // 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)); } else { deliveryIntents.add(null); } 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); } if (LogTag.DEBUG_SEND) { Log.v(TAG, "sendMessage sendIntent: " + intent); } sentIntents.add(PendingIntent.getBroadcast(mContext, requestCode, intent, 0)); } try { smsManager.sendMultipartTextMessage(mDest, mServiceCenter, messages, sentIntents, deliveryIntents);//发送信息的方法。 } catch (Exception ex) { Log.e(TAG, "SmsMessageSender.sendMessage: caught", ex); throw new MmsException("SmsMessageSender.sendMessage: caught " + ex + " from SmsManager.sendTextMessage()"); } if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE) || LogTag.DEBUG_SEND) { log("sendMessage: address=" + mDest + ", threadId=" + mThreadId + ", uri=" + mUri + ", msgs.count=" + messageCount); } return false; }
1、得到SmsMessager对象。
2、将信息内容按照GSM/CDMA等制式分段,也就是看需要几条信息将上述信息内容发送出去。因为每条信息有字数限制
3、更新短信类别,更新到发件箱
4、构建两个Peddingintent,这两个Penddingintent用于在信息发送完成之后,或者在接收到短信状态报告之后回调发送广播,这个广播也会在这个service中处理。
5、调用SmsManager发送信息。
以上就是短信发送的上层逻辑,从smsManager.sendMultipartTextMessage之后就要进入到framework层。
我们今天就先介绍短信发送流程的上层逻辑流程,关于后面的两部分,请关注更新。