`

短信接收--短信的接收流程应用层

 
阅读更多

短信的接收流程应用层

1、源文件

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

2、图解

短信接收的时序图:

注意:SeviceHandler是SmsReceiverService的内部类,SmsReceiver是PrivlegedSmsReceiver的父类;

3、详细分析

3.1 PrivilegedSmsReceiver到SmsReceiverService

1)PrivilegedSmsReceiver这个接收器从中间才能获取数据
     PrivilegedSmsReceiver是一个广播接收器并且继承自SmsReceiver,在AndroidManifest.xml 中有如下声明:
[plain] view plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  1. private final ConversationListAdapter.OnContentChangedListener mContentChangedListener =  
  2.      new ConversationListAdapter.OnContentChangedListener() {  
  3.      public void onContentChanged(ConversationListAdapter adapter) {  
  4.          startAsyncQuery();  
  5.      }  
  6.  };  
重新调用startAsyncQuery() 该方法刷新。
 2)会话列表的更新
         看到上面监听器所做的工作大家应该明白啦,会话列表的更新靠的就是这个监听器,当内容发生改变就会重新查询,界面进行刷新,到此为止 短信的界面刷新完成。
特 别注意:该情况是用户在短信会话列表这个界面,如果不在这个界面大概还有其他两种情况:  1、在某个会话中;2、没有进入mms程序。对于前一种情况会在下面继续分析,对于后一种情况我想也不用多说在这种情况下会走activity的声明周期 函数,在onstart方法里进行查询显示前面已经提到。那还有一种特殊的情况就是在从某个会话中返回到会话列表时的处理。下面请看ConversationList的声明:
[plain] view plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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 plaincopy
  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函数重新查询达到刷新的效果。

4、总结

     短信的接收大致过程就是这样,对于上面提到的替换短信,该情况暂时不清楚,有些细节描述的很粗糙,希望大家多提意见,一起研究研究
分享到:
评论

相关推荐

    Visual C++/Turbo C串口通信编程实践及源代码-2

    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 某车载无线调度系统实例...

    Visual C++/Turbo C串口通信编程实践及源代码-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 某车载无线调度系统实例...

    Visual C++/Turbo C串口通信编程实践 及源代码-1

    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 某车载无线调度系统实例...

    联通大数据有限公司-招投标数据分析报告.pdf

    4 北京 2019-11-28 2019年联通大数据"政务数据共享开放应用开发"产品合作伙伴招募项目 5 镇江 2019-11-28 2019年联通大数据有限公司外呼中心公开招募项目 6 镇江 2019-11-21 2019年联通大数据外部数据接口短信通道...

    android 完全中文版 开发应用详解

    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知识库

    将 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. 薪酬...

    Visual C++_Turbo C 串口通信编程实践.(电子工业.龚建伟.熊光明) 源码光盘

    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 某车载无线调度系统实例介绍...

    Visual C++_Turbo C 串口通信编程实践.(电子工业.龚建伟.熊光明) 第二版 电子版

    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应用开发权威指南(黄晓庆)

    《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 ...

    C#项目开发案例全程实录(第2版)下载地址

     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 常用短语管理...

    java源码包---java 源码 大量 实例

    6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用...

    无线通讯系统设计方案.doc

    四、设备连接框图 五、设备工作流程 本系统使用的是异频半双工工作方式,即中继台的收接收频率与手持对讲机的 发射频率相同,中继台的发射频率与手持对讲机的接收频率相同。链路部分使用同 频工作方式,即两链路...

    中国移动客户服务系统功能需求规范(1.0版)

    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. 薪酬...

    JAVA上百实例源码以及开源项目

    6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个EJB对象的count()方法,保证Bean正常被激活和钝化,EJB对象是用...

    成百上千个Java 源码DEMO 4(1-4是独立压缩包)

    Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个...

    成百上千个Java 源码DEMO 3(1-4是独立压缩包)

    Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个...

    JAVA上百实例源码以及开源项目源代码

    Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个...

Global site tag (gtag.js) - Google Analytics