短信的接收流程应用层
1、源文件
这部分代码在packages/apps/Mms下,涉及的主要类:
- com.android.mms.transaction.PrivilegedSmsReceiver
- com.android.mms.transaction.SmsReceiver
- com.android.mms.transaction.SmsReceiverService
- com.android.mms.transaction.MessagingNotification
2、图解
注意:SeviceHandler是SmsReceiverService的内部类,SmsReceiver是PrivlegedSmsReceiver的父类;
PrivilegedSmsReceiver是一个广播接收器并且继承自SmsReceiver,在AndroidManifest.xml 中有如下声明:
- <intent-filter>
- <action android:name="android.provider.Telephony.SMS_RECEIVED" />
- </intent-filter>
- protected void dispatchPdus(byte[][] pdus) {
- Intent intent = new Intent(Intents.SMS_RECEIVED_ACTION);
- intent.putExtra("pdus", pdus);
- intent.putExtra("encoding", getEncoding());
- intent.putExtra("sub_id", mPhone.getSubscription()); //Subscription information to be passed in an intent
- dispatch(intent, "android.permission.RECEIVE_SMS");
- }
- void dispatch(Intent intent, String permission) {
- mWakeLock.acquire(WAKE_LOCK_TIMEOUT);
- mContext.sendOrderedBroadcast(intent, permission, mResultReceiver,
- this, Activity.RESULT_OK, null, null);
- }
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String SMS_RECEIVED_ACTION =
- "android.provider.Telephony.SMS_RECEIVED";
到这大家应该明白PrivilegedSmsReceiver会接收到中间层的广播,并且该广播很不一般它承载了短信的内容,它从中间层接过接力棒继续向上传递。
2)PrivilegedSmsReceiver传递数据
PrivilegedSmsReceiver从中间层获取到短信的数据后会调用onReceiveWithPrivilege()方法,该方法定义在它的父类SmsReceiver中。该方法没有做太多的操作,仅仅是传递消息,一下是其核心代码:
- protected void onReceiveWithPrivilege(Context context, Intent intent, boolean privileged) {
- if (!privileged && (intent.getAction().equals(Intents.SMS_RECEIVED_ACTION)
- || intent.getAction().equals(Intents.SMS_CB_RECEIVED_ACTION))) {
- return;
- }
- intent.setClass(context, SmsReceiverService.class);
- intent.putExtra("result", getResultCode());
- beginStartingService(context, intent);
- }
3)SmsReceiverService处理
SmsReceiverService它是一个服务,当它开启的时候:首先是在onCreate中初始化,其中初始化最重要的工作就是实例化ServiceHandler对象,ServiceHandler该类是SmsReceiverService的一个内部类,继承自Handler,以下是它的定义代码:
走到这我们可以看出该对象的重要性,即是处理短信真正的苦力,我们继续看是怎么调用到这的。
- private final class ServiceHandler extends Handler {
- public ServiceHandler(Looper looper) {
- super(looper);
- }
- /**
- * Handle incoming transaction requests.
- * The incoming requests are initiated by the MMSC Server or by the MMS Client itself.
- */
- @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 (MESSAGE_SENT_ACTION.equals(intent.getAction())) {
- handleSmsSent(intent, error);
- } else 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);
- }
- }
onCreate走完请看 onStartCommand方法:
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- mResultCode = intent != null ? intent.getIntExtra("result", 0) : 0;
- Message msg = mServiceHandler.obtainMessage();
- msg.arg1 = startId;
- msg.obj = intent;
- mServiceHandler.sendMessage(msg);
- return Service.START_NOT_STICKY;
- }
4)ServiceHandler处理接收到的短信
根据不同的action处理,由于这里是短信的接收SMS_RECEIVED_ACTION,所以调用 handleSmsReceived(intent, error)方法,该方法的处理逻辑如下所示:
说 明在insertMessage方法时会判断当前是替换还是插入,对于替换短信,笔者不是很清楚在什么情况下会走这条路。 blockingUpdateNewMessageIndicator方法会用notification提醒用户,并且在方法内会判断当前用户是否需要显 示发送报告。
3.2 刷新会话列表
走到上面的代码,短信已经入库,但界面的刷新是如何实现的了?
1)会话列表的初始化
ConversationList继承自ListActivity,用于显示短信的会话列表,在该类的onStart方法里有调用了一个重要的方法startAsyncQuery()方法:
- private void startAsyncQuery() {
- try {
- setTitle(getString(R.string.refreshing));
- setProgressBarIndeterminateVisibility(true);
- Conversation.startQueryForAll(mQueryHandler, THREAD_LIST_QUERY_TOKEN);
- } catch (SQLiteException e) {
- SqliteWrapper.checkSQLiteException(this, e);
- }
- }
startQueryForAll方法定义:
- public static void startQueryForAll(AsyncQueryHandler handler, int token) {
- handler.cancelOperation(token);
- handler.startQuery(token, null, sAllThreadsUri,
- ALL_THREADS_PROJECTION, null, null, Conversations.DEFAULT_SORT_ORDER);
- }
这里会使用mQueryHandler去查询数据库,查询完后会回调该对象的onQueryComplete方法,在该方法里填充了mListAdapter,使得会话列表得以显示到界面上。以下代码是其定义:
- private final class ThreadListQueryHandler extends AsyncQueryHandler {
- public ThreadListQueryHandler(ContentResolver contentResolver) {
- super(contentResolver);
- }
- @Override
- protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
- switch (token) {
- case THREAD_LIST_QUERY_TOKEN:
- mListAdapter.changeCursor(cursor);
- setTitle(mTitle);
- setProgressBarIndeterminateVisibility(false);
- if (mNeedToMarkAsSeen) {
- mNeedToMarkAsSeen = false;
- Conversation.markAllConversationsAsSeen(getApplicationContext());
- // Database will be update at this time in some conditions.
- // Wait 1s and ensure update complete.
- mQueryHandler.postDelayed(new Runnable() {
- public void run() {
- // Delete any obsolete threads. Obsolete threads are threads that aren't
- // referenced by at least one message in the pdu or sms tables.
- Conversation.asyncDeleteObsoleteThreads(mQueryHandler,
- DELETE_OBSOLETE_THREADS_TOKEN);
- }
- }, 1000);
- }
- break;
- default:
- Log.e(TAG, "onQueryComplete called with unknown token " + token);
- }
- }
- }
- private void initListAdapter() {
- mListAdapter = new ConversationListAdapter(this, null);
- mListAdapter.setOnContentChangedListener(mContentChangedListener);
- setListAdapter(mListAdapter);
- getListView().setRecyclerListener(mListAdapter);
- }
- private final ConversationListAdapter.OnContentChangedListener mContentChangedListener =
- new ConversationListAdapter.OnContentChangedListener() {
- public void onContentChanged(ConversationListAdapter adapter) {
- startAsyncQuery();
- }
- };
重新调用startAsyncQuery() 该方法刷新。
2)会话列表的更新
看到上面监听器所做的工作大家应该明白啦,会话列表的更新靠的就是这个监听器,当内容发生改变就会重新查询,界面进行刷新,到此为止 短信的界面刷新完成。
看到上面监听器所做的工作大家应该明白啦,会话列表的更新靠的就是这个监听器,当内容发生改变就会重新查询,界面进行刷新,到此为止 短信的界面刷新完成。
特 别注意:该情况是用户在短信会话列表这个界面,如果不在这个界面大概还有其他两种情况: 1、在某个会话中;2、没有进入mms程序。对于前一种情况会在下面继续分析,对于后一种情况我想也不用多说在这种情况下会走activity的声明周期 函数,在onstart方法里进行查询显示前面已经提到。那还有一种特殊的情况就是在从某个会话中返回到会话列表时的处理。下面请看ConversationList的声明:
- <activity android:name=".ui.ConversationList"
- android:label="@string/app_label"
- android:configChanges="orientation|keyboardHidden"
- android:launchMode="singleTop">
- @Override
- protected void onNewIntent(Intent intent) {
- // Handle intents that occur after the activity has already been created.
- startAsyncQuery();
- }
3.23刷新会话内容
刷新ui除了刷新会话列表之外,还有一种情况就是当用户在某个会话时,这时该会话接收到新的消息,这时需要刷新会话的内容,这是怎么实现的?
用于会话显示的activity:ComposeMessageActivity;用于显示会话的短信内容组件: MessageListView;填充listview的adapter是:MessageListAdapter
1)初始化
ComposeMessageActivity的onCreate方法调用initialize方法,initialize方法再调用initMessageList()完成初始化
- private void initMessageList() {
- if (mMsgListAdapter != null) {
- return;
- }
- String highlightString = getIntent().getStringExtra("highlight");
- Pattern highlight = highlightString == null
- ? null
- : Pattern.compile("\\b" + Pattern.quote(highlightString), Pattern.CASE_INSENSITIVE);
- // Initialize the list adapter with a null cursor.
- mMsgListAdapter = new MessageListAdapter(this, null, mMsgListView, true, highlight);
- mMsgListAdapter.setOnDataSetChangedListener(mDataSetChangedListener);
- mMsgListAdapter.setMsgListItemHandler(mMessageListItemHandler);
- mMsgListView.setAdapter(mMsgListAdapter);
- mMsgListView.setItemsCanFocus(false);
- mMsgListView.setVisibility(View.VISIBLE);
- mMsgListView.setOnCreateContextMenuListener(mMsgListMenuCreateListener);
- mMsgListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- if (view != null) {
- ((MessageListItem) view).onMessageListItemClick();
- }
- }
- });
- }
说明:MessageListAdapter定义了一个监听器当数据发生变化的时候回调监听器的onContentChanged的方法,该方法会重新查询该会话相关的内容并刷新显示,以下是其定义:
- private final MessageListAdapter.OnDataSetChangedListener
- mDataSetChangedListener = new MessageListAdapter.OnDataSetChangedListener() {
- public void onDataSetChanged(MessageListAdapter adapter) {
- mPossiblePendingNotification = true;
- }
- public void onContentChanged(MessageListAdapter adapter) {
- startMsgListQuery();
- }
- };
ComposeMessageActivity 的onStart函数里面调用一个重要的方法loadMessageContent();该方法会继续调用startMsgListQuery(),在上 面的adapter的监听器里当内容有变动时回调函数也会调用该方法,以下代码是该方法做的具体工作:
- private void startMsgListQuery() {
- Uri conversationUri = mConversation.getUri();
- if (conversationUri == null) {
- return;
- }
- if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
- log("for " + conversationUri);
- }
- // Cancel any pending queries
- mBackgroundQueryHandler.cancelOperation(MESSAGE_LIST_QUERY_TOKEN);
- try {
- // Kick off the new query
- mBackgroundQueryHandler.startQuery(
- MESSAGE_LIST_QUERY_TOKEN, null, conversationUri,
- PROJECTION, null, null, null);
- } catch (SQLiteException e) {
- SqliteWrapper.checkSQLiteException(this, e);
- }
- }
- @Override
- protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
- switch(token) {
- case MESSAGE_LIST_QUERY_TOKEN:
- // Set last sub used in this conversation thread.
- if (cursor.getCount() > 0) {
- cursor.moveToLast();
- mLastSubInConv = cursor.getInt(COLUMN_SUB_ID); //TODO: ADD SUBSCRIPION HERE
- cursor.moveToPosition(-1);
- } else {
- mLastSubInConv = SUBSCRIPTION_ID_INVALID;
- }
- int newSelectionPos = -1;
- long targetMsgId = getIntent().getLongExtra("select_id", -1);
- if (targetMsgId != -1) {
- cursor.moveToPosition(-1);
- while (cursor.moveToNext()) {
- long msgId = cursor.getLong(COLUMN_ID);
- if (msgId == targetMsgId) {
- newSelectionPos = cursor.getPosition();
- break;
- }
- }
- }
- mMsgListAdapter.changeCursor(cursor);
- if (newSelectionPos != -1) {
- mMsgListView.setSelection(newSelectionPos);
- }
- if (cursor.getCount() == 0 && !isRecipientsEditorVisible() && !mSentMessage) {
- initRecipientsEditor();
- }
- mTextEditor.requestFocus();
- mConversation.blockMarkAsRead(false);
- mConversation.setMessageCount(cursor.getCount());
- return;
- }
- }
3)刷新
刷新就很简单啦,当数据有变化的时候会触发OnDataSetChangedListener这个监听器,这个监听器会调用onContentChanged函数重新查询达到刷新的效果。
4、总结
短信的接收大致过程就是这样,对于上面提到的替换短信,该情况暂时不清楚,有些细节描述的很粗糙,希望大家多提意见,一起研究研究
相关推荐
10.1.4 用串口收发sms短信编程的一些讨论 283 10.2 计算机与rabbit 2000嵌入式系统通信编程实例 286 10.2.1 rabbit 2000微处理器介绍 286 10.2.2 动态c(dynamic c)语言介绍 287 10.2.3 某车载无线调度系统实例...
10.1.4 用串口收发sms短信编程的一些讨论 283 10.2 计算机与rabbit 2000嵌入式系统通信编程实例 286 10.2.1 rabbit 2000微处理器介绍 286 10.2.2 动态c(dynamic c)语言介绍 287 10.2.3 某车载无线调度系统实例...
10.1.4 用串口收发sms短信编程的一些讨论 283 10.2 计算机与rabbit 2000嵌入式系统通信编程实例 286 10.2.1 rabbit 2000微处理器介绍 286 10.2.2 动态c(dynamic c)语言介绍 287 10.2.3 某车载无线调度系统实例...
4 北京 2019-11-28 2019年联通大数据"政务数据共享开放应用开发"产品合作伙伴招募项目 5 镇江 2019-11-28 2019年联通大数据有限公司外呼中心公开招募项目 6 镇江 2019-11-21 2019年联通大数据外部数据接口短信通道...
13.4 android 应用层api参考文档 315 第14章 android应用程序的主要方面 317 14.1 应用的基本控制 318 14.1.1 ui元素及其控制 318 14.1.2 屏幕间的跳转 320 14.1.3 弹出对话框和菜单 324 14.1.4 样式的设置 328 ...
将 ASP.NET 2.0 应用程序服务配置为使用 SQL Server 2000 或 SQL Server 2005 ASP.NET 2.0 中的数据源控件 使用 ASP.NET 2.0 ObjectDataSource 控件 ASP.NET 2.0 的内部变化 使用SQL Cache Dependency 代替 ...
2.4.3. 便笺接收 84 2.4.4. 便笺维护 85 第3章. 人力资源管理系统 86 3.1. 概述 86 3.2. 人事管理 86 3.2.1. 职位体系管理 87 3.2.2. 人力资源规划 88 3.2.3. 招聘甄选管理 90 3.2.4. 人事档案管理 92 3.2.5. 薪酬...
10.1.4 用串口收发SMS短信编程的一些讨论 283 10.2 计算机与Rabbit 2000嵌入式系统通信编程实例 286 10.2.1 Rabbit 2000微处理器介绍 286 10.2.2 动态C(Dynamic C)语言介绍 287 10.2.3 某车载无线调度系统实例介绍...
10.1.4 用串口收发SMS短信编程的一些讨论 283 10.2 计算机与Rabbit 2000嵌入式系统通信编程实例 286 10.2.1 Rabbit 2000微处理器介绍 286 10.2.2 动态C(Dynamic C)语言介绍 287 10.2.3 某车载无线调度系统实例介绍...
《OPhone应用开发权威指南》2010 黄晓庆 博文视点 broadview 第1章 OPhone平台概述 1 1.1 OPhone的架构 1 1.1.1 Linux内核 2 1.1.2 本地库 2 1.1.3 OPhone运行环境 3 1.1.4 Widget运行环境 3 1.1.5 应用程序框架 4 ...
2.8.3 短信接收模块实现过程 76 2.8.4 单元测试 80 2.9 电话簿管理模块设计 82 2.9.1 电话簿管理模块概述 82 2.9.2 电话簿管理模块技术分析 83 2.9.3 电话簿管理模块实现过程 84 2.10 常用短语管理...
6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用...
四、设备连接框图 五、设备工作流程 本系统使用的是异频半双工工作方式,即中继台的收接收频率与手持对讲机的 发射频率相同,中继台的发射频率与手持对讲机的接收频率相同。链路部分使用同 频工作方式,即两链路...
2.4.3. 便笺接收 84 2.4.4. 便笺维护 85 第3章. 人力资源管理系统 86 3.1. 概述 86 3.2. 人事管理 86 3.2.1. 职位体系管理 87 3.2.2. 人力资源规划 88 3.2.3. 招聘甄选管理 90 3.2.4. 人事档案管理 92 3.2.5. 薪酬...
6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用...
Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个...
Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个...
Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个...