短信接收--短信的接收流程應用層

短信的接收流程應用層

一、源文件

這部分代碼在packages/apps/Mms下,涉及的主要類:
[plain]  view plain copy
  1. com.android.mms.transaction.PrivilegedSmsReceiver  
  2. com.android.mms.transaction.SmsReceiver  
  3. com.android.mms.transaction.SmsReceiverService  
  4. com.android.mms.transaction.MessagingNotification  

二、圖解

短信接收的時序圖:

注意:SeviceHandler是SmsReceiverService的內部類,SmsReceiver是PrivlegedSmsReceiver的父類;

三、詳細分析

3.1 PrivilegedSmsReceiver到SmsReceiverService

1) PrivilegedSmsReceiver這個接收器從中間才能獲取數據
      PrivilegedSmsReceiver是一個廣播接收器而且繼承自SmsReceiver,在AndroidManifest.xml 中有以下聲明:
[plain]  view plain copy
  1. <intent-filter>  
  2.             <action android:name="android.provider.Telephony.SMS_RECEIVED" />  
  3.         </intent-filter>  
android.provider.Telephony.SMS_RECEIVED該action在那被使用到了?若是你們有看過度析中間層的接收流程的童鞋就很清楚了,中間層處理接收到的短信的時侯最後會調用到SMSDispatcher的protected void dispatchPdus(byte[][] pdus) 方法,讓咱們回眸一下:
[plain]  view plain copy
  1. protected void dispatchPdus(byte[][] pdus) {  
  2.      Intent intent = new Intent(Intents.SMS_RECEIVED_ACTION);  
  3.      intent.putExtra("pdus", pdus);  
  4.      intent.putExtra("encoding", getEncoding());  
  5.      intent.putExtra("sub_id", mPhone.getSubscription()); //Subscription information to be passed in an intent  
  6.      dispatch(intent, "android.permission.RECEIVE_SMS");  
  7.  }  
你們確定會問dispatch又幹了些什麼了? 請看下面:
[plain]  view plain copy
  1. void dispatch(Intent intent, String permission) {  
  2.      mWakeLock.acquire(WAKE_LOCK_TIMEOUT);  
  3.      mContext.sendOrderedBroadcast(intent, permission, mResultReceiver,  
  4.              this, Activity.RESULT_OK, null, null);  
  5.  }  
看到這就不用我多說了吧,很顯然是發送了一個叫作Intents.SMS_RECEIVED_ACTION的廣播,那又有人刨根問底兒了,上面兩個值同樣嗎?請看intent中對該變量的定義:
[plain]  view plain copy
  1. @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)  
  2.         public static final String SMS_RECEIVED_ACTION =  
  3.                 "android.provider.Telephony.SMS_RECEIVED";  
到這你們應該明白PrivilegedSmsReceiver會接收到中間層的廣播,而且該廣播很不通常它承載了短信的內容,它從中間層接過接力棒繼續向上傳遞。
2) PrivilegedSmsReceiver傳遞數據
     PrivilegedSmsReceiver從中間層獲取到短信的數據後會調用onReceiveWithPrivilege()方法,該方法定義在它的父類SmsReceiver中。該方法沒有作太多的操做,僅僅是傳遞消息,一下是其核心代碼:
[plain]  view plain copy
  1. protected void onReceiveWithPrivilege(Context context, Intent intent, boolean privileged) {  
  2.       if (!privileged && (intent.getAction().equals(Intents.SMS_RECEIVED_ACTION)  
  3.               || intent.getAction().equals(Intents.SMS_CB_RECEIVED_ACTION))) {  
  4.           return;  
  5.       }  
  6.   
  7.       intent.setClass(context, SmsReceiverService.class);  
  8.       intent.putExtra("result", getResultCode());  
  9.       beginStartingService(context, intent);  
  10.   }  
它將處理短信的任務交到SmsReceiverService的手中,SmsReceiverService纔是真正幹活的傢伙。
3)SmsReceiverService處理
SmsReceiverService它是一個服務,當它開啓的時候:首先是在onCreate中初始化,其中初始化最重要的工做就是實例化ServiceHandler對象,ServiceHandler該類是SmsReceiverService的一個內部類,繼承自Handler,如下是它的定義代碼:
[plain]  view plain copy
  1. private final class ServiceHandler extends Handler {  
  2.         public ServiceHandler(Looper looper) {  
  3.             super(looper);  
  4.         }  
  5.         /**  
  6.          * Handle incoming transaction requests.  
  7.          * The incoming requests are initiated by the MMSC Server or by the MMS Client itself.  
  8.          */  
  9.         @Override  
  10.         public void handleMessage(Message msg) {  
  11.             int serviceId = msg.arg1;  
  12.             Intent intent = (Intent)msg.obj;  
  13.             if (intent != null) {  
  14.                 String action = intent.getAction();  
  15.   
  16.                 int error = intent.getIntExtra("errorCode", 0);  
  17.   
  18.                 if (MESSAGE_SENT_ACTION.equals(intent.getAction())) {  
  19.                     handleSmsSent(intent, error);  
  20.                 } else if (SMS_RECEIVED_ACTION.equals(action)) {  
  21.                     handleSmsReceived(intent, error);  
  22.                 } else if (SMS_CB_RECEIVED_ACTION.equals(action)) {  
  23.                     handleCbSmsReceived(intent, error);  
  24.                 } else if (ACTION_BOOT_COMPLETED.equals(action)) {  
  25.                     handleBootCompleted();  
  26.                 } else if (TelephonyIntents.ACTION_SERVICE_STATE_CHANGED.equals(action)) {  
  27.                     handleServiceStateChanged(intent);  
  28.                 } else if (ACTION_SEND_MESSAGE.endsWith(action)) {  
  29.                     handleSendMessage(intent);  
  30.                 }  
  31.             }  
  32.             // NOTE: We MUST not call stopSelf() directly, since we need to  
  33.             // make sure the wake lock acquired by AlertReceiver is released.  
  34.             SmsReceiver.finishStartingService(SmsReceiverService.this, serviceId);  
  35.         }  
  36.     }  
走到這咱們能夠看出該對象的重要性,便是處理短信真正的苦力,咱們繼續看是怎麼調用到這的。
onCreate走完請看  onStartCommand方法:
   
[plain]  view plain copy
  1. @Override  
  2. public int onStartCommand(Intent intent, int flags, int startId) {  
  3.     mResultCode = intent != null ? intent.getIntExtra("result", 0) : 0;  
  4.     Message msg = mServiceHandler.obtainMessage();  
  5.     msg.arg1 = startId;  
  6.     msg.obj = intent;  
  7.     mServiceHandler.sendMessage(msg);  
  8.     return Service.START_NOT_STICKY;  
  9. }  
看到嗎,到這它已經順利脫手交給 ServiceHandler對象去異步處理。
4)ServiceHandler處理接收到的短信
  根據不一樣的action處理,因爲這裏是短信的接收SMS_RECEIVED_ACTION,因此調用 handleSmsReceived(intent, error)方法,該方法的處理邏輯以下所示:

 說明在insertMessage方法時會判斷當前是替換仍是插入,對於替換短信,筆者不是很清楚在什麼狀況下會走這條路。blockingUpdateNewMessageIndicator方法會用notification提醒用戶,而且在方法內會判斷當前用戶是否須要顯示發送報告。

3.2 刷新會話列表

        走到上面的代碼,短信已經入庫,但界面的刷新是如何實現的了?
1)會話列表的初始化
        ConversationList繼承自ListActivity,用於顯示短信的會話列表,在該類的onStart方法裏有調用了一個重要的方法startAsyncQuery()方法:
[plain]  view plain copy
  1. private void startAsyncQuery() {  
  2.     try {  
  3.         setTitle(getString(R.string.refreshing));  
  4.         setProgressBarIndeterminateVisibility(true);  
  5.   
  6.         Conversation.startQueryForAll(mQueryHandler, THREAD_LIST_QUERY_TOKEN);  
  7.     } catch (SQLiteException e) {  
  8.         SqliteWrapper.checkSQLiteException(this, e);  
  9.     }  
  10. }  
解析:
startQueryForAll方法定義:
[plain]  view plain copy
  1. public static void startQueryForAll(AsyncQueryHandler handler, int token) {  
  2.     handler.cancelOperation(token);  
  3.     handler.startQuery(token, null, sAllThreadsUri,  
  4.             ALL_THREADS_PROJECTION, null, null, Conversations.DEFAULT_SORT_ORDER);  
  5. }  
這裏會使用mQueryHandler去查詢數據庫,查詢完後會回調該對象的onQueryComplete方法,在該方法裏填充了mListAdapter,使得會話列表得以顯示到界面上。如下代碼是其定義:
[plain]  view plain copy
  1. private final class ThreadListQueryHandler extends AsyncQueryHandler {  
  2.         public ThreadListQueryHandler(ContentResolver contentResolver) {  
  3.             super(contentResolver);  
  4.         }  
  5.         @Override  
  6.         protected void onQueryComplete(int token, Object cookie, Cursor cursor) {  
  7.             switch (token) {  
  8.             case THREAD_LIST_QUERY_TOKEN:  
  9.                 mListAdapter.changeCursor(cursor);  
  10.                 setTitle(mTitle);  
  11.                 setProgressBarIndeterminateVisibility(false);  
  12.   
  13.                 if (mNeedToMarkAsSeen) {  
  14.                     mNeedToMarkAsSeen = false;  
  15.                     Conversation.markAllConversationsAsSeen(getApplicationContext());  
  16.                     // Database will be update at this time in some conditions.  
  17.                     // Wait 1s and ensure update complete.  
  18.                     mQueryHandler.postDelayed(new Runnable() {  
  19.                         public void run() {  
  20.                             // Delete any obsolete threads. Obsolete threads are threads that aren't  
  21.                             // referenced by at least one message in the pdu or sms tables.  
  22.                             Conversation.asyncDeleteObsoleteThreads(mQueryHandler,  
  23.                                     DELETE_OBSOLETE_THREADS_TOKEN);  
  24.                         }  
  25.                     }, 1000);  
  26.                 }  
  27.                 break;  
  28.                        default:  
  29.                 Log.e(TAG, "onQueryComplete called with unknown token " + token);  
  30.             }  
  31.         }  
  32.   
  33.            }  
這裏爲何要特別提到該對象了,後面更新的操做與它有着密不可分的關係。 mListAdapter該對象是ConversationListAdapter的對象,該對象在ConversationList的oncreate方法裏調用 initListAdapter()進行的初始化。 initListAdapter()對adapter進行初始化:
[plain]  view plain copy
  1. private void initListAdapter() {  
  2.        mListAdapter = new ConversationListAdapter(this, null);  
  3.        mListAdapter.setOnContentChangedListener(mContentChangedListener);  
  4.        setListAdapter(mListAdapter);  
  5.        getListView().setRecyclerListener(mListAdapter);  
  6.    }  
mListAdapter.setOnContentChangedListener(mContentChangedListener);是當adapter的內容發生變化,會去執行監聽器的onContentChanged的方法。那爲了弄清楚 mContentChangedListener的定義,查看如下代碼
[plain]  view plain copy
  1. private final ConversationListAdapter.OnContentChangedListener mContentChangedListener =  
  2.      new ConversationListAdapter.OnContentChangedListener() {  
  3.      public void onContentChanged(ConversationListAdapter adapter) {  
  4.          startAsyncQuery();  
  5.      }  
  6.  };  
從新調用startAsyncQuery() 該方法刷新。
 2)會話列表的更新
         看到上面監聽器所作的工做你們應該明白啦,會話列表的更新靠的就是這個監聽器,當內容發生改變就會從新查詢,界面進行刷新,到此爲止 短信的界面刷新完成。
特別注意:該狀況是用戶在短信會話列表這個界面,若是不在這個界面大概還有其餘兩種狀況:  一、在某個會話中;二、沒有進入mms程序。對於前一種狀況會在下面繼續分析,對於後一種狀況我想也不用多說在這種狀況下會走activity的聲明周期函數,在onstart方法裏進行查詢顯示前面已經提到。那還有一種特殊的狀況就是在從某個會話中返回到會話列表時的處理。下面請看ConversationList的聲明:
[plain]  view plain copy
  1. <activity android:name=".ui.ConversationList"  
  2.             android:label="@string/app_label"  
  3.             android:configChanges="orientation|keyboardHidden"  
  4.             android:launchMode="singleTop">  
屬性是singleTop,你們都知道這種狀況會去調用onNewIntent方法:
[plain]  view plain copy
  1. @Override  
  2.   protected void onNewIntent(Intent intent) {  
  3.       // Handle intents that occur after the activity has already been created.  
  4.       startAsyncQuery();  
  5.   }  
該方法又會去從新查詢刷新界面。

3.23刷新會話內容

刷新ui除了刷新會話列表以外,還有一種狀況就是當用戶在某個會話時,這時該會話接收到新的消息,這時須要刷新會話的內容,這是怎麼實現的?
 用於會話顯示的activity:ComposeMessageActivity;用於顯示會話的短信內容組件: MessageListView;填充listview的adapter是:MessageListAdapter
1)初始化
ComposeMessageActivity的onCreate方法調用initialize方法,initialize方法再調用initMessageList()完成初始化
[plain]  view plain copy
  1. private void initMessageList() {  
  2.     if (mMsgListAdapter != null) {  
  3.         return;  
  4.     }  
  5.     String highlightString = getIntent().getStringExtra("highlight");  
  6.     Pattern highlight = highlightString == null  
  7.         ? null  
  8.         : Pattern.compile("\\b" + Pattern.quote(highlightString), Pattern.CASE_INSENSITIVE);  
  9.     // Initialize the list adapter with a null cursor.  
  10.     mMsgListAdapter = new MessageListAdapter(this, null, mMsgListView, true, highlight);  
  11.     mMsgListAdapter.setOnDataSetChangedListener(mDataSetChangedListener);  
  12.     mMsgListAdapter.setMsgListItemHandler(mMessageListItemHandler);  
  13.     mMsgListView.setAdapter(mMsgListAdapter);  
  14.     mMsgListView.setItemsCanFocus(false);  
  15.     mMsgListView.setVisibility(View.VISIBLE);  
  16.     mMsgListView.setOnCreateContextMenuListener(mMsgListMenuCreateListener);  
  17.     mMsgListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {  
  18.         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {  
  19.             if (view != null) {  
  20.                 ((MessageListItem) view).onMessageListItemClick();  
  21.             }  
  22.         }  
  23.     });  
  24. }  
說明:MessageListAdapter定義了一個監聽器當數據發生變化的時候回調監聽器的onContentChanged的方法,該方法會從新查詢該會話相關的內容並刷新顯示,如下是其定義:
[plain]  view plain copy
  1. private final MessageListAdapter.OnDataSetChangedListener  
  2.                 mDataSetChangedListener = new MessageListAdapter.OnDataSetChangedListener() {  
  3.     public void onDataSetChanged(MessageListAdapter adapter) {  
  4.         mPossiblePendingNotification = true;  
  5.     }  
  6.     public void onContentChanged(MessageListAdapter adapter) {  
  7.         startMsgListQuery();  
  8.     }  
  9. };  

2) MessageListAdapter內容的初始化
ComposeMessageActivity的onStart函數裏面調用一個重要的方法loadMessageContent();該方法會繼續調用startMsgListQuery(),在上面的adapter的監聽器裏當內容有變更時回調函數也會調用該方法,如下代碼是該方法作的具體工做:
[plain]  view plain copy
  1. private void startMsgListQuery() {  
  2.     Uri conversationUri = mConversation.getUri();  
  3.     if (conversationUri == null) {  
  4.         return;  
  5.     }  
  6.     if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {  
  7.         log("for " + conversationUri);  
  8.     }  
  9.     // Cancel any pending queries  
  10.     mBackgroundQueryHandler.cancelOperation(MESSAGE_LIST_QUERY_TOKEN);  
  11.     try {  
  12.         // Kick off the new query  
  13.         mBackgroundQueryHandler.startQuery(  
  14.                 MESSAGE_LIST_QUERY_TOKEN, null, conversationUri,  
  15.                 PROJECTION, null, null, null);  
  16.     } catch (SQLiteException e) {  
  17.         SqliteWrapper.checkSQLiteException(this, e);  
  18.     }  
  19. }  
分析:該方法所作的工做就是使用mBackgroundQueryHandler查詢數據庫( mBackgroundQueryHandler是一個AsyncQueryHandler的對象),查詢完成後會回調 mBackgroundQueryHandler該對象的onQueryComplete方法,如下是其核心代碼:
[plain]  view plain copy
  1. @Override  
  2.         protected void onQueryComplete(int token, Object cookie, Cursor cursor) {  
  3.             switch(token) {  
  4.                 case MESSAGE_LIST_QUERY_TOKEN:  
  5.                     // Set last sub used in this conversation thread.  
  6.                     if (cursor.getCount() > 0) {  
  7.                         cursor.moveToLast();  
  8.                         mLastSubInConv = cursor.getInt(COLUMN_SUB_ID); //TODO: ADD SUBSCRIPION HERE  
  9.                         cursor.moveToPosition(-1);  
  10.                     } else {  
  11.                         mLastSubInConv = SUBSCRIPTION_ID_INVALID;  
  12.                     }  
  13.                     int newSelectionPos = -1;  
  14.                     long targetMsgId = getIntent().getLongExtra("select_id", -1);  
  15.                     if (targetMsgId != -1) {  
  16.                         cursor.moveToPosition(-1);  
  17.                         while (cursor.moveToNext()) {  
  18.                             long msgId = cursor.getLong(COLUMN_ID);  
  19.                             if (msgId == targetMsgId) {  
  20.                                 newSelectionPos = cursor.getPosition();  
  21.                                 break;  
  22.                             }  
  23.                         }  
  24.                     }  
  25.                     mMsgListAdapter.changeCursor(cursor);  
  26.                     if (newSelectionPos != -1) {  
  27.                         mMsgListView.setSelection(newSelectionPos);  
  28.                     }  
  29.                     if (cursor.getCount() == 0 && !isRecipientsEditorVisible() && !mSentMessage) {  
  30.                         initRecipientsEditor();  
  31.                     }  
  32.                     mTextEditor.requestFocus();  
  33.                     mConversation.blockMarkAsRead(false);  
  34.                     mConversation.setMessageCount(cursor.getCount());  
  35.                     return;  
  36.   
  37.             }  
  38.         }  
代碼雖多,但其核心就是對mMsgListAdapter的內容從新賦值刷新界面完畢。
3)刷新
    刷新就很簡單啦,當數據有變化的時候會觸發OnDataSetChangedListener這個監聽器,這個監聽器會調用onContentChanged函數從新查詢達到刷新的效果。
相關文章
相關標籤/搜索