转载请注明出处: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

点击事件:

@Overridepublic 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 buildString 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 messageLog.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() {@Overridepublic 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() {@Overridepublic void run() {preSendSmsWorker(conv, msgText, recipientsInUI);updateSendStats(conv);}}, "WorkingMessage.send SMS").start();}// update the Recipient cache with the new to address, if it's differentRecipientIdCache.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 sendif (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 mismatchLogTag.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在哪里创建的那?
我们跳转到处理SqliteWrapper.query的地方,根据Uri信息,我们找到了处理的逻辑代码:

MmsSmsProvider.java

query方法:

case URI_THREAD_ID:List<String> recipients = uri.getQueryParameters("recipient");cursor = getThreadId(recipients);break;

我们继续深入,进入到getThreadId方法内:

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 casesfor (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 recipientscursor = 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);}

现在我们得到了ThreadId,我们回到我们之前的地方,继续看看在sendSmsWork方法中的逻辑:

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 countRecycler.getSmsRecycler().deleteOldMessagesByThreadId(mActivity, threadId);} catch (Exception e) {Log.e(TAG, "Failed to send SMS message, threadId=" + threadId, e);}mStatusListener.onMessageSent();MmsWidgetProvider.notifyDatasetChanged(mActivity);}

new一个SmsMessageSender,将联系人的发送地址,发送内容,会话id(threadId)作为参数,然后调用sendMessage方法。

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 destinationsreturn 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 outmContext.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);}

根据Uri,找到相应的ContentProvider,然后信息会被插入数据库 sms表,信息包括,短信内容,发送时间,信息类型(type = Sms.MESSAGE_TYPE_QUEUED;),等。

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));

注意此处的action:

SmsReceiverService.ACTION_SEND_MESSAGE

我们看看广播中的处理逻辑:

SmsReceiver.java

@Overridepublic 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)) {//接受信息的actionreturn;}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);}

首先开启一个新的线程,线程的looper用于处理ServiceHandler中的作业。

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;}

将从广播过来的Intent信息包装秤Message作业发送到ServiceHandler中处理:

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 databasefinal 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();}}

我们刚才将信息插入到sms数据表中,type = Sms.MESSAGE_TYPE_QUEUED,那么此处将这些信息加载上来,遍历发送。

在发送过程中,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层。

我们今天就先介绍短信发送流程的上层逻辑流程,关于后面的两部分,请关注更新。


关注我的技术公众号,查看更多优质技术文章推送

微信扫一扫下方二维码即可关注:

Android短彩信源码解析-短信发送流程(一)相关推荐

  1. http://a.codekk.com/detail/Android/grumoon/Volley 源码解析

    http://a.codekk.com/detail/Android/grumoon/Volley 源码解析

  2. BAT高级架构师合力熬夜15天,肝出了这份PDF版《Android百大框架源码解析》,还不快快码住。。。

    前言 为什么要阅读源码? 现在中高级Android岗位面试中,对于各种框架的源码都会刨根问底,从而来判断应试者的业务能力边际所在.但是很多开发者习惯直接搬运,对各种框架的源码都没有过深入研究,在面试时 ...

  3. Android通知系统源码解析

    Android通知系统源码解析 1. 概述 2. 流程图 2.1. 发送通知流程图 3. 源码解析 3.1. 使用通知--APP进程 3.1.1. 创建通知: 3.1.2. 发送(更新)通知: 3.1 ...

  4. Android Gradle Plugin 源码解析(上)

    一.源码依赖 本文基于: android gradle plugin版本: com.android.tools.build:gradle:2.3.0 gradle 版本:4.1 Gradle源码总共3 ...

  5. Android之EasyPermissions源码解析

    转载请标明出处:[顾林海的博客] 个人开发的微信小程序,目前功能是书籍推荐,后续会完善一些新功能,希望大家多多支持! 前言 我们知道在Android中想要申请权限就需要在AndroidManifest ...

  6. Android之AsyncTask源码解析

    转载请标明出处:[顾林海的博客] 个人开发的微信小程序,目前功能是书籍推荐,后续会完善一些新功能,希望大家多多支持! ##前言 AsyncTask是一种轻量级的异步任务类,内部封装了Thread和Ha ...

  7. Android之DiskLruCache源码解析

    转载请标明出处: http://blog.csdn.net/hai_qing_xu_kong/article/details/73863258 本文出自:[顾林海的博客] 个人开发的微信小程序,目前功 ...

  8. Android Hawk的源码解析,一款基于SharedPreferences的存储框架

    转载请标注:http://blog.csdn.net/friendlychen/article/details/76218033 一.概念 SharedPreferences的使用大家应该非常熟悉啦. ...

  9. Android之LocalBroadcastManager源码解析

    转载请标明出处:[顾林海的博客] 个人开发的微信小程序,目前功能是书籍推荐,后续会完善一些新功能,希望大家多多支持! 前言 广播想必大家都不陌生,日常开发中同一个APP中的多个进程之间需要进行传输信息 ...

最新文章

  1. ASP.net 验证码(C#)
  2. CentOS7.2 安装Docker
  3. jquery压缩图片插件
  4. python必学的模块_Python必学的模块有哪些?
  5. Java总结:SpringBoot的使用cmd命令进行Gradle构建
  6. oracle-3-子查询和常用函数
  7. 基于C#的学生选课管理系统
  8. python中pygame怎么安_Python中pygame安装方法图文详解
  9. 1097: 蛇行矩阵
  10. 5G基站硬件架构及演进研究
  11. 新浪微博批量取消关注
  12. nova系列可以升级鸿蒙吗,鸿蒙官网再度放出新消息,华为nova 6系列以上7款机型可以升级...
  13. 微博爬虫及简单数据分析
  14. 神经科学(第三版)书籍,神经系统相关书籍
  15. 信息安全——ELGamal数字签名方案的实现
  16. CentOS 7安装chrome
  17. 好用的vue瀑布流插件-vue-masonry
  18. 凯撒密码(Caesar)
  19. JavaScript数据结构和算法笔记一(前八章)
  20. 山东省谷歌地球高程DEM等高线下载

热门文章

  1. 集成方法,或者叫做组合方法(Ensemble methods)介绍(一)
  2. 3D格式转换工具HOOPS Exchange最全技术指南(三):4大功能特征与典型使用场景
  3. python实验总结与反思_警示与反思丨什么是Python网络爬虫?看这篇清晰多了!
  4. 15高级链接构建策略和技巧,以提高您的SEO
  5. 还在用Navicat?这款开源的数据库管理工具界面更炫酷!
  6. Python编程:通过百度地图接口抓取机构的地址和电话信息
  7. 透过“最牛”年报,聊聊优然牧业的“最佳养牛模式”
  8. HTML CSS游戏官网网页模板 大学生游戏介绍网站毕业设计 DW游戏主题网页模板下载 游戏娱乐网页成品代码
  9. Chris and Magic Square CodeForces - 711B
  10. css3 logo 自上而下动画 渐渐出现