SAP CRM几个常用的使用技巧

SAP CRM交互记录中创建日期,实际日期的开始日期和结束日期

系统创建日期为点击结束按钮,系统保存单据的时间。
日期参数文件中的实际日期的开始时间为点击接受按钮,即开始电话通话的时间。
日期参数文件中的实际日期的结束时间为点击结束按钮,系统保存单据的时间。
日期参数文件中若定义创建日期,则也为点击接受按钮的时间。
注:该基础是实际日期的日期规则是系统标准的。用当前日期时间规则也可以。


SAP CRM呼叫中心外呼清单呼叫时间和持续时间记录

选中清单中的客户,即开始记录call time
点击重新计划按钮即开始计算持续时间。(即使后面点了结束按钮,也不再计算)
或者,没有重新计划的,点击结束按钮,计算持续时间。


SAP CRM 交互作用历史搜索标准时间功能

1、交互记录:使用标准计划时间或实际时间(开始部分)。
2、投诉:使用标准通知收据时间。


SAP CRM组织、个人及关系电话存储

BUT000存BP,如果是个人,则有PERSNUMBER。
BUT020存BP和地址号的关系。
在表ADR2中,根据ADDRNUMBER或PERSNUMBER均可以查找到电话。
若ADDRNUMBER是组织地址号,PERSNUMBER是个人号,则该电话为关系电话。 

SAP CRM状态和处理该状态对应的时间以及状态时间持续的配置

路径:事物-服务请求的设置-定义持续时间的设置
这里定义了每一个状态和对应的日期类型和持续时间。
日期类型表示该状态在什么时候产生的。
持续时间定义了该状态持续了多长时间,该状态重复出现则持续时间累加。
持续时间的单位在日期参数文件中定义。不定义单位则默认为小时。
请注意实际业务中持续时间和状态的实际关系。


SAP CRM没有事物代码权限时的处理方法。

SE37
ALINK_CALL_TRANSACTION
输入T-CODE.执行。


SAP没有相应修改删除权限时,对数据库表(T-code:SE16)通过DEBUG进行数据处理

1、SE16。/H激活调试
2、选中需要处理的数据,点击显示(或者F7)
3、SHIFT+F5。或者选择Breakpoints-Breakpoint at-Breakpoint at statement
4、在ABAP Cmnds中输入CASE
5、按F8执行,进入CASE行。
6、在Variable中输入CODE,回车,显示为SHOW(因为开始进入是选择的是显示)
7、将SHOW修改为EDIT,或者DELE,即可进入SE16的标准修改或删除界面,进行修改或删除操作。

java多线程 sleep()和wait()的区别

接触了一些多线程的东西,还是从java入手吧。
相信看这篇文章的朋友都已经知道进程和线程的区别,也都知道了为什么要使用多线程了。
这两个方法主要来源是,sleep用于线程控制,而wait用于线程间的通信,与wait配套的方法还有notify和notifyAll.
区别一:
sleep是Thread类的方法,是线程用来 控制自身流程的,比如有一个要报时的线程,每一秒中打印出一个时间,那么我就需要在print方法前面加上一个sleep让自己每隔一秒执行一次。就像个闹钟一样。
wait是Object类的方法,用来线程间的通信,这个方法会使当前拥有该对象锁的进程等待知道其他线程调用notify方法时再醒来,不过你也可以给他指定一个时间,自动醒来。这个方法主要是用走不同线程之间的调度的。
区别二 :
关于锁的释放 ,在这里假设大家已经知道了锁的概念及其意义。调用sleep方法不会释放锁(自己的感觉是sleep方法本来就是和锁没有关系的,因为他是一个线程用于管理自己的方法,不涉及线程通信)
JDK 7 中的解释:
“public static void sleep(long millis)
throws InterruptedException
Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds, subject to the precision and accuracy of system timers and schedulers.The thread does not lose ownership of any monitors.
public final void wait() throws InterruptedException
Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object. In other words, this method behaves exactly as if it simply performs the call wait(0).The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the notify method the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.“
调用wait方法会释放当前线程的锁(其实线程间的通信是靠对象来管理的,所有操作一个对象的线程是这个对象通过自己的wait方法来管理的,就好像这个对象是电视机,三个人是三个线程,那么电视机的遥控器就是这个锁,假如现在A拿着遥控器,电视机调用wait方法,那么A就交出自己的遥控器,由jVM虚拟机调度,遥控器该交给谁。)【我想到一个好玩的例子:如果A拿遥控器的期间,他可以用自己的sleep每隔十分钟调一次电视台,而在他调台休息的十分钟期间,遥控器还在他的手上~】
区别三:
使用区域
由于wait函数的特殊意义,所以他是应该放在同步语句块中的,这样才有意义 。
注意:两个方法都需要抛出异常
个人见解:有sleep和wait的第二个区别,引起了我对Java线程机制的一个疑问,目前还没有看过JDk这方面的源码(其实看了,是木有看懂),线程的同步管理,是不是由对象在调度,如果是对象在调度,那么JDK 1.5新引入的ReentrantLock机制就比synchronized关键字更值得提倡。因为他更能反映出这么一个机制来。好多人不能理解wait和sleep的区别,我认为就是因为synchronized关键字的影响。当然自己还不懂JAVA的线程具体实现,留作疑问以后有时间继续研究吧

Eclipse 快捷键大全

1. Ctrl+左键
这个是大多数人经常用到的,用来查看变量、方法、类的定义
2. Ctrl+O
查看一个类的纲要,列出其方法和成员变量。提示 :再多按一次Ctrl+O ,可以列出该类继承的方法和变量。
助记 :"O"--->"Outline"--->"纲要"
3. Ctrl+T
查看一个类的继承关系树,是自顶向下的,再多按一次Ctrl+T, 会换成自底向上的显示结构。
提示 :选中一个方法名,按Ctrl+T,可以查看到有这个同名方法的父类、子类、接口。
助记 :"T"------->"Tree"----->"层次树"
4.Alt+左右方向键
我们经常会遇到看代码时Ctrl+左键,层层跟踪,然后迷失在代码中的情况,这时只需要按“Alt+左方向键”就可以退回到上次阅读的位置,同理,按“Alt+右方向键”会前进到刚才退回的阅读位置,就像浏览器的前进和后退按钮一样。
5.Ctrl+Alt+H
如果你想知道一个类的方法到底被那些其他的类调用,那么请选中这个方法名,然后按“Ctrl+Alt+H”,Eclipse就会显示出这个方法被哪些方法调用,最终产生一个调用关系树。
Ctrl+D: 删除当前行 
Ctrl+Alt+↓ 复制当前行到下一行(复制增加)
Ctrl+Alt+↑ 复制当前行到上一行(复制增加)
Alt+↓ 当前行和下面一行交互位置(特别实用,可以省去先剪切,再粘贴了)
Alt+↑   当前行和上面一行交互位置(同上)
Alt+← 前一个编辑的页面
Alt+→ 下一个编辑的页面(当然是针对上面那条来说了)
Alt+Enter 显示当前选择资源(工程,or 文件 or文件)的属性
Shift+Enter 在当前行的下一行插入空行(这时鼠标可以在当前行的任一位置,不一定是最后)
Shift+Ctrl+Enter 在当前行插入空行(原理同上条)
Ctrl+Q   定位到最后编辑的地方
Ctrl+L 定位在某行 (对于程序超过100的人就有福音了)
Ctrl+M 最大化当前的Edit或View (再按则反之)
Ctrl+/   注释当前行,再按则取消注释
Ctrl+O   快速显示 OutLine
Ctrl+T   快速显示当前类的继承结构
Ctrl+W 关闭当前Editer
Ctrl+K   参照选中的Word快速定位到下一个
Ctrl+E 快速显示当前Editer的下拉列表(如果当前页面没有显示的用黑体表示)
Ctrl+/(小键盘) 折叠当前类中的所有代码
Ctrl+×(小键盘) 展开当前类中的所有代码
Ctrl+Space代码助手完成一些代码的插入(但一般和输入法有冲突,可以修改输入法的热键,也可以暂用Alt+/来代替)
Ctrl+Shift+E 显示管理当前打开的所有的View的管理器(可以选择关闭,激活等操作)
Ctrl+J 正向增量查找(按下Ctrl+J后,你所输入的每个字母编辑器都提供快速匹配定位到某个单词,如果没有,则在stutesline中显示没有找到了,查一个单词时,特别实用,这个功能Idea两年前就有了)
Ctrl+Shift+J 反向增量查找(和上条相同,只不过是从后往前查)
Ctrl+Shift+F4 关闭所有打开的Editer
Ctrl+Shift+X  把当前选中的文本全部变味大写
Ctrl+Shift+Y  把当前选中的文本全部变为小写
Ctrl+Shift+F 格式化当前代码
Ctrl+Shift+P 定位到对于的匹配符(譬如{}) (从前面定位后面时,光标要在匹配符里面,后面到前面,则反之)
下面的快捷键是重构里面常用的,本人就自己喜欢且常用的整理一下(注:一般重构的快捷键都是Alt+Shift开头的了)
Alt+Shift+R 重命名 (是我自己最爱用的一个了,尤其是变量和类的Rename,比手工方法能节省很多劳动力)
Alt+Shift+M 抽取方法 (这是重构里面最常用的方法之一了,尤其是对一大堆泥团代码有用)
Alt+Shift+C 修改函数结构(比较实用,有N个函数调用了这个方法,修改一次搞定)
Alt+Shift+L 抽取本地变量( 可以直接把一些魔法数字和字符串抽取成一个变量,尤其是多处调用的时候)
Alt+Shift+F 把Class中的local变量变为field变量 (比较实用的功能)
Alt+Shift+I 合并变量(可能这样说有点不妥Inline)
Alt+Shift+V 移动函数和变量(不怎么常用)
Alt+Shift+Z 重构的后悔药(Undo)
Ctrl+Shift+U 选择选中的文字后非常类似于UE的列表查询
Ctrl+Alt+H 查看一个函数被其他函数调用的关系层次


第一部分 常用快捷键说明
   eclipse的使用。Eclipse有很多快捷键,及有用的功能,快捷键的使用可以一定程度上提高开发的速度。说明如下:
常用的快捷键(没有按照Eclipse菜单顺序)
     A:Ctrl+k          向下查找选中的字符串
     B:Ctrl+shift+K      向上查找选中的字符串
     C:Ctrl+shift+↑↓      向上向下查找一个函数和变量
     D:双击”{“或”}”      找到相对应的”}”和”{“
                   鼠标的位置必须在”{“的或者”}”的右边位置
     E: Ctrl+shift+P            (同上)    找到相对应的”}”和”{“
     F:Ctrl+shift+M    将鼠标放在出错的变量或者类上,即可导入未知的import
                   右键菜单中也有 source ->add import 同样功能
     G:F2            查看完整的函数帮助信息,并且可以复制粘贴文字
       F3            找到变量的定义,      
       F4            找到接口方法的具体实现类.    
     H: Ctrl +/      注释选择的代码段,加”//”(对于大段代码临时注释有用)
     I: Ctrl+\        去掉注释 也就是去掉选择的代码前面的 “//”
     
     J: Alt +/        帮助
     K:Alt+→        切换到前进的下一个视图或者操作.
     L : ALT +←      切换到后退的下一个视图或者操作
     M : 按下Ctrl,      用鼠标指向要寻找的变量定义.即可查找变量 和F3同样的功能.
     N :Ctrl +→←      以完整的单词为单位移动光标。
     O:CtrL + Shift + →← 以完整的单词为单位向后选中文本。
     P: Ctrl +Q      定位最后编辑的地方,这个不管你当前打开是哪个页面.找到最后编辑的页面,并定位.
第二部分 菜单以及部分快捷键说明
   下面分菜单详细说明不太常用的快捷键以及一些不常用但很重要的功能
edit菜单 (编辑操作处理)  
         
Ctrl+J        按键后,即进入向下快速查找状态,直接单击你要查找的字符串即可.eclipse将随着你的按键直接定位您输入的连续字符.找到之后,分别按↑↓即可找到下一个或上一个要查找的字符.按左右箭头键或者ESC即可推出快速查找状态.
Ctrl + shift +J        按键后,即进入向上快速查找状态, 其他同上
Ctrl +1        快速修正功能.出错的变量处,按键后提示你如何修正错            误.,并提供了几种供你选择的方案,如创建出错的变量,创建类.等
Ctrl + shift+/    也就是Ctrl+?         此功能只有在调用函数时起作用,将光标放在函数的参数输入处,按下前面的快捷键,函数的参数定义出现在鼠标上方,方便查看参数类型.
Alt+shift+↑↓→←       属于快速选择文本的快捷键.鼠标所在字符直接可以用快捷键选择变量字符,和整个完整语句,段落..等
上箭头: 结构化的扩大选择的范围
下箭头:结构化的缩小选择的范围
左箭头:向上扩大
右箭头:向下扩大

Source菜单(源代码处理)
Ctrl + Shift +O       将import中的包排序,自动按照名称排序。规则化
     Surround withtry/catch       选择一段有效的代码,单击这个菜单,可以产生try块代码,同样的功能也可以单击代码编辑框左边的错误指示栏目中的错误点.然后从弹出的菜单中选择try/catch
ExternalizeStrings       此项功能为国际化应用提供了快速的处理。将所有(允许指定)的字符串都放入一个资源文件,并定义一个类读取资源文件的类。并修改了java代码的字符串读取方法。
Refactor(源码处理)
 
Change MethodSignature       改变方法签名 将鼠标放在要改变的方法上选择这个菜单,单击。在弹出的对话框中改变方法的参数等数据,确定后系统自动按照方法的新参数重新调整此方法的所有调用,参数也被修改了。
PullDown       将父类中的方法放到子类中去。
Pullup       将子类中的方法放到父类中去。很方便的。
ExtractInterface       从一个类导出允公共方法(允许选择)自动生成接口的java文件
Inline(Alt + shift +I)       其实就相当于宏替换。将用到这个变量,方法,常量的所有地方全部替换为直接使用内容的方法,可以先预览。
Extract Local Variable
Extract Constant
Extract Method
      变量替换,创建一个新的变量,替换所有指定的变量。其他类同extract功能
Convert Local Variable toField       将局部定义的变量瞬间定义到上面,的作为类变量。并可以设置。很方便的。一定要试试呦。先定义一个局部变量并赋值,鼠标放到上面即可,单击菜单中的选项即可。右键菜单中也有。我试过了。很不错。特别适合我们定义错误信息时使用。
EncapsulateField       将数据封装为函数格式,鼠标放到变量上。单击此菜单,总生成了读取方法并修改了变量的引用方式
    
Navigate菜单(菜单中说明已经很明显了,下面个别说明)
       Show in > PackageExplorer       Package 定位当前类所在的包并打开包
navigator 定位当前类所在的路径并打开
Ctrl+Alt+H    Open Call Hierarchy.
      将鼠标放到方法上.点击此菜单.将找到所有调用此方法的位置.并列在下面
.(右键菜单也有此功能)
Ctrl +O            ShowOutline      打开outline窗口.代替右边的那个窗口用来快速定位函数.需要注意的是:使用嵌入的outline时.里面的方法时按照实际的顺序排列的,而弹出式的窗口中时分别按照方法和变量的名称顺序排列的.
Ctrl +.        定位下一个有问题的地方
Ctrl +,        定位上一个有问题的地方
Ctrl +L        快速定位某行,要求输入行号的.
Ctr +Q        定位最后编辑的地方
Search 菜单
Ctrl +H        查找功能强大.跨文件查询.
Ctrl + shift +U       很常用的一个功能.(只在这个当前文件查找)
选择你要搜索的字符.按下组合键.下面列表列出了所有出现了这个字符串的行.不用一个一个搜索了呀.并用白色标识显示在右边标识定位栏.
 
第三部分 提高Eclipse的性能配置.以及一些常用配置说明
    1,由于eclipse对内存的使用使用的是默认的配置.如果您的机器内存允许,虚拟机器可用内存.
     Window->Preferences->Java->InstalledJREs->右侧->JDK..->EDIT->DefaultJVM arguments: -> 改为 –Xms200m –Xmx200m
    2,关闭不用的工程.
    3,如果很讨厌自动弹出的帮助代码,也很影响效率.可以改为手动控制.
   Window->Preferences->Java->Editor->CodeAssist -> Enable auto activation 不勾选
第四部分 自动创建代码的模板说明
Window->Preferences->Java->Editor->Templates下
自动代码生成后,编辑器处于模板状态.模板状态不同于正常编辑状态.
模板状态下编辑自动生成的变量,相关的代码部分会跟随做正确的变动. 例如for(inti=0;i<=100;i++){} 改变变量i的名称.则
后面的i也会跟着变动.按下ESC退出模板状态.

アプリケーションサーバへタブ区切りでファイルを出力する

1. 出力内容をタブ区切りでセットする
2. 出力内容をアプリケーションサーバのファイルへ渡す
CONSTANTS:
GCST_TAB TYPE C VALUE CL_ABAP_CHAR_LTILITIES=>HORIZONTAL_TAB.
CONCATENATE A B C
INTO D
SEPARATED BY GCST_TAB.
TRANSFER D TO LC_FILE_PATH.


4798059625
¥5,280

ABAP F4ヘルプ表示

検索ヘルプでテーブルの内容を表示させる:
モジュール名:F4IF_INT_TABLE_VALUE_REQUEST
例:
CALL FUNCTION ‘F4IF_INT_TABLE_VALUE_REQUEST’
EXPORTING
RETFIELD = ‘ZZVCHID’
DYNPROOG = SY-REPID
DYNPNR = SY-DYNNR
DYNPROFIELD = ‘ZZVCHID’
VALUE = LC_VALUE          ← LC_VALUE = PC_ZZVCHID
VALUE_ORG = ‘S’
TABLES
VALUE_TAB = LIT_ZZVCHID_HELP ← MASTER_ ZZVCHID
EXCEPTIONS
PARAMETER_ERROR = 1
NO_VALUES_FOUND = 2
OTHERS = 3.

ABAPからPDFファイルを作成する

用件: 
・ レポートをバックグラウンド処理にてPDFファイルとしてファイルサーバーに保存する
方法:
・汎用モジュールCONVERT_ABAPSPOOLJOB_2_PDFを実行
(参照PG:PTRA_WEB_EXPENSE_FORM_PDF_GET)
手順:
 1. ファイルサーバーに臨時保存ファイルののパス、ファイルを作成する
    ・ パス、ファイル名を指定して、Commondの Open、Transferを利用してファイルを作成する
 2. スプールを作成する
  ① GET_PRINT_PARAMETERSを利用して、パラメータをセットする
     取得値: DATA_SET  ← スプールテーブル名の初期値
           LIST_NAME ← スプール依頼名の初期値
           OUT_PARAMETERS ← コールされた印刷パラメータ一覧
  ② スプールを作成する(SUBMIT rep TO SAP-SPOOL)
     例:
       SUBMIT XXX
           TO SAP-SPOOL
           SPOOL PARAMETERS OUT_PARAMETERS
           WITHOUT SPOOL DYNPRO
           AND RETURN.
    ・ABAP 命令の NEW-PAGE PRINT ON を使用し 、レポートからの印刷機能を有効にし、ス 
     プールを作成する方法もある。
 3. スプールIDを取得する(汎用モジュール:RSPO_FIND_SPOOL_REQUESTS)
     I/F: DATA_SET、LIST_NAME
     取得値: L_SPOOL_IDS
 4. スプールをPDFに変換する(汎用モジュール:CONVERT_ABAPSPOOLJOB_2_PDF)
     I/F: SRC_SPOOLID = ‘XXX’ “  ← 3.で取得したスプール番号
 5. 汎用モジュールDOWNLOADを利用して、PDFファイルを取得する
問題点: サーバーへ格納するのは直接DOWNLOADを利用できるかなぁ?
→ 
① サーバーへ格納する時、OPEN DATASETとTRANSFERを利用する
EXAMPLE:
LC_OUTPUT_FILE_PATH = ‘/SAP/UP/TESTPDF.PDF.
OPEN DATASET LC_OUTPUT_FILE_PATH FOR OUTPUT IN BINARY MODE.
LOOP AT LIT_PDF_TABLE INTO LFC_PDF_TABLE
TRANSFER LFC_PDF_TABLE TO LC_OUTPUT_FILE_PATH.
ENDLOOP.
CLOSE DATASET LC_OUTPUT_FILE_PATH.
② GUI_UPLOADはローカルのファイルを内部テーブルに格納する

BADIについて

 ユーザーEXIT、カスタマーEXIT、ビジネスアドイン(BAdi)、OPEN FIなど拡張の技術は様々であるが、これらが存在する理由としては、企業固有の業務や特殊なロジックを標準のR/3の機能だけで表現していくには限界があり、それを実現していくのユーザーモディフィケーションである。
 各手法の自由度、実装方法は違うが、バージョンアップ、ノート適用、バッチ適用などの影響を最小限にするため、BAdiはよりスタンダード的な技術になっていく。
BAdiの実装方法
① 定義[SE18]
② 実装[SE19]
③ クラスビルダ[SE24]
④ 実装の有効化[SE24]
BAdi定義の検索方法
①[SE84]
②実装内容確認:
 i.SE37 SXV_GET_CLIF_BY_NAME
ii.確認TRを起動

ABAPローカルファイルの選択方法

・ ローカルファイルを指定する際には、選択画面でファイルパス+ファイル名の形で入力する
・ XXXファイルのドロップダウンが押下さるた場合、GUIでローカルファイルを選択する
Sample:
① 画面ファイル名を取得
  GET_DNYP_VALUE
   I_FIELD : 選択画面で指定されたローカルファイル
   I_REPID : SY-CPROG(ABAP プログラム、外部プロシージャの呼び出し元)
  I_DYNNR: SY-DYNNR(ABAP プログラム、現在のDynpro番号)
  O_VALUE  → LC_FILE
② ディレクトリパスの算出
  SEARCH LC_FILE FOR GC_PATH ← ¥
  WHILE SY-SUBRC = 0.
    FIND GC_PATH IN CECTION OFFSET LI_OFF OF LC_FILE
    MATCH OFFSET LI_LEN.
    IF SY-SUBRC = 0.
      LC_FILE = LC_FILE+0(LI_OFF).
     ELSE.
      LC_OFF = LI_LEN + 1.
    ENDIF.
  ENDWHILE.
③ ファイル名選択
  TMP_GUI_FILE_OPEN_DIALOG
   WINDOW_TITLE : 固定値(アップロードファイルetc.)
  DEFAULT_FILENAME: 選択画面上で指定したローカルファイル
  FILE_FILTER: 固定値(*.TXT etc.)
  MULTISELECTION: SPACE(固定値)
  INITIAL_DIRECTORY: ②で取得したファイルパス
  FILE_TABLE
  RC
   → RCが-1でない且つ処理が正常に終了した場合、FILE_TABLEの1行目からファイル名を取得する
※ ファイル選択メソッドも使える
  CALL METHOD CL_GUI_FRONTEND_SERVICES=>FILE_OPEN_DIALOG

ALV形式でレポートを出力

① フィールドカタログ取得
   CALL FUNCTION ‘REUSE_ALV_FIELDCATALOG_MERGE’
② ALVレイアウト作成
   GC_LAYOUT-COLWIDTH_OPTIMIZE = GCST_ON “カラム幅調整
   GC_LAYOUT-ZEBRA = GCST_ON “ゼブラ
   GC_LAYOUT-DETAIL_POPUT = P_DETAIL “詳細表示
③ ALV出力
   CALL FUNCTION ‘REUSE_ALV_GRID_DISPLAY’
通貨を表示する時、
① 当項目が通貨であることを宣言
② 当通貨が参照していいる通貨はなんであるか宣言する(etc. JPY? USD? MSEGの通貨科目)

Spoolの制御

SP01で他の部署が出した内容を見せたくない場合(SP02は自分のものしか見れない)
→ SP01 とSP02の中間みたいの機能を実現したい
①スプール依頼(SP01o)のトランザクションをコピーし、バリアントトランザクションを設定
②①でセットしたトランザクションをロールに割りあって、担当者に権限変更

JOB設定

・SM36 JOB設定(JOB設定者、JOB実行者を別々に設定することが可能)
・SM37 設定したJOBを確認(確認する内容はデフォルト値として、同じ開発機、検証機、本番機全クライアントの内容であり、表示形式の編集により、各クライアントの内容を確認できる)

BADIの確認方法

例: 請求書照合
現象:転記する際、ある会社コードで許可されるが、ある会社でエラーメッセージを表示する。
確認手順:
 反方向確認
  ① 転記する前、デバッグ状態に入ってから、転記ボタンを押す。
  ② BreakPointを設置:Message
  ③ 関連BADIを確認
  ④ SE18、SE19
 正方向確認:
  ① SE18で検索
  ② SE19でソース確認

ABAPトランザクションコード作成

・SE93
・トランザクションコードを実行する際のバリアントがあれば、ここで一緒に定義していく

日付型(DATUM)項目

問題:
 日付項目に「 / / 」の内容をセットされ、IF 日付項目 = INITIAL.に入れない。
調査:
 2010/10/02 
 ・ Clear → 0000/00/00 = Initial
 ・ Space →   / /   Initial
原因:
 日付項目の初期値は「00000000」である、これは「 / / 」とは違うため、IF分に入れない。
解決案:
 ① 日付項目のリセットは「00000000」を代入するかClearを用いる
 ② IFを書く際、SAPCEでクリアしようというミスを防ぐため、以下の構文で対応する
   IF 日付項目 IS INITIAL OR
  日付項目 = SPACE

削除Talbe entry の移送

1.SE09により移送番号を作成(ブランク、ワークベンチ)
2.上位の移送番号をダブルクリックして、Object タブで変更
3.プログラムID:【R3TR、Object Type:TABU、Object Name:Table ID
4.Table keyをインプット(削除したエントリのキー)
5.保存(下位いらない移送番号を削除)

SAP 変更文書

1, 変更管理
変更管理がされる条件は、
①項目のデータエレメントで、変更文書フラグがONになっていること。
②オブジェクトクラスに
テーブルが割り当たっていること。
>ツール>ABAPワークベンチ>開発>他のツール>SCDO:変更文書
2. 関連するトランザクション: SCDO
変更履歴テーブル(CDHDR・CDPOS)におけるオブジェクトクラス。
【共通】
FACTORYCAL: 稼働日カレンダ
HOLIDAYCAL: 祝日カレンダ
HOLIDAY: 祝日変更
KLASSE: 分類クラス
FEATURE: 分類特性
【LO:ロジ一般】
MATERIAL: 品目マスタ
CHARGE: 品目ロット
COND_A: 価格条件マスタ
【SD:販売管理】
DEBI: 得意先マスタ
KLIM: 得意先与信管理
EMBK: ライセンスマスタ
VERKBELEG: 受注伝票
LIEFERUNG: 出荷伝票
FAKTBELEG: 請求伝票
VBEX: 受注伝票ライセンス
【MM:在庫/購買管理】
KRED: 仕入先マスタ
INFOSATZ: 購買情報
ORDERBUCH; 供給元一覧
BANF: 購買依頼
EINKBELEG: 購買発注
REVISION: 購買管理のバージョン
【PP:生産計画/管理】
EQUI: 設備マスタ
STUE: BOM
STUE_V: BOM
【FI:財務会計】
SACH: 勘定コードマスタ
ANLA: 資産マスタ
BANK: 銀行マスタ
IBAN: 国際銀行預金口座番号
BELEG: 会計伝票
【CO:管理会計】
KSTAR: 原価要素マスタ
KOSTL: 原価センタマスタ
LSTAR: 活動タイプ
RKAUFTRAG: 内部指図
CMDT_PC: 利益センタマスタ
PRCTR: 利益センタマスタ
SETS: 管理会計マスタグループ
3. 関連する汎M
CHANGEDOCUMENT_READ_HEADERS
この汎用モジュールは、特定の変更文書オブジェクトについて変更文書番号と関連ヘッダ情報を読み込みます。さまざまなパラメータ ( 変更者、変更日、変更時刻 ) によって、検索範囲を限定することができます。
CHANGEDOCUMENT_READ_POSITIONS
この汎用モジュールは、特定の変更文書オブジェクト番号について変更文書明細を読み込み、変更前の値または変更後の値を、そのタイプに応じてフォーマットします。

abap性能优化——利用凭证的number ranger提高abap程序性能

当我们的程序需要搜索某些凭证的时侯,常常会涉及到比较庞大的数据表,比如BSEG, MSEG, VBRP等等,如果这时又无法获取凭证号码等一些关键字段的值,那么程序必然会消耗非常多的资源。这种情况下,下面的方法或许可以得到意想不到的效果。
前提:
-  知道被搜索凭证的类型
-  无法利用数据表的关键字搜索凭证
解决方案:
-   利用已知的凭证类型,得到该类型凭证的号码范围,从数据表中得到该号码范围内的所有凭证,存入内表,通过操作该内表进一步对其他的条件字段进行筛选。
步骤:
1. 读取数据表TVAK得到凭证类型的number ranger object.
2. 读取数据表NRIV得到该number ranger object的当前号码范围
3. 利用SELECT语句的BETWEEN子句,基于凭证号码字段,读取被搜索凭证的数据表,并存入内表
4. 进一步根据其他条件筛选内表。

ABAP两位数字金额转化为大写格式

 IV_MONEY ABSIV_MONEY ).
  DATASCR(30TYPE CRES(60TYPE C,FEN(2TYPE .
  DATALEN TYPE IC1 TYPE IC2 TYPE IC3 TYPE IC4 TYPE I.
  DATAD1(1TYPE CD2(1TYPE CD3 TYPE I.
  DATADIGIT(2)  TYPE CWEIGHT(2TYPE C.
  DATARULE1(20TYPE VALUE '零壹贰叁肆伍陆柒捌玖'.
  DATARULE2(30TYPE VALUE '分角元拾佰仟万拾佰仟亿拾佰仟万'.
  SCR IV_MONEY * 100.
  CONDENSE SCR NO-GAPS.
  IF SCR '0'.
    RES '零元'.
  ELSE.
    LEN STRLENSCR ).
    C1 0.
    D1 '0'.
    CLEAR RES.
    DO LEN TIMES.
      C1 C1 + 1.
      C2 LEN C1.
      D2 SCR+C2(1.
      IF D2 '0'.
        D3 0.
      ELSE.
        D3 D2.
      ENDIF.
      DIGIT RULE1+D3(1.
      C3 C1 .
      WEIGHT RULE2+C3(1.
      IF D2 '0'.
        IF C1 3.
          DIGIT ''.
        ELSEIF C1 7.
          DIGIT ''.
          IF LEN > 10 .
            C4 LEN 10.
            IF SCR+C4(4'0000'.
              WEIGHT ''.
            ENDIF.
          ENDIF.
        ELSEIF C1 11.
          DIGIT ''.
        ELSEIF D1 '0'.
          DIGIT ''.
          WEIGHT ''.
        ELSE.
          WEIGHT ''.
        ENDIF.
      ENDIF.
      CONCATENATE DIGIT WEIGHT RES INTO RES .
      D1 D2.
    ENDDO.
  ENDIF.
  LEN STRLENRES 1.
  FEN RES+LEN(1).
  IF FEN <> '分' .
    CONCATENATE RES '整' INTO EV_MONEY.
  ELSE.
    EV_MONEY RES.
  ENDIF.

Android官方开发文档Training系列课程中文版:网络操作之XML解析

扩展标记语言(XML)是一系列有序编码的文档。它是一种很受欢迎的互联网数据传输格式。像需要频繁更新内容的网站来说,比如新闻站点或者博客,需要经常更新它们的XML源,以使外部程序可以保持内容的同步变化。对于含有网络连接态的APP应用来说,上传及解析XML数据是一个通用的任务。这节课将会学习如何解析XML文档及如何使用XML中的数据。

选择解析器

我们推荐使用XmlPullParser解析器,在Android上它是一种高效的可维护的解析器。Android中含有该接口的两个实现:
两个选择都可以。这里使用的是ExpatPullParser。

分析源

解析源的第一步就是判断哪种属性是你所关注的。解析器会将这些属性所对应的数据提取出,将剩余的部分忽略。
下面是示例APP中的部分摘录。每个实例都是以entry的形式出现在源里,并且entry会包含若干个嵌套标签。
<?xml version="1.0" encoding="utf-8"?> 
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule" ...">     
<title type="text">newest questions tagged android - Stack Overflow</title>
...
    <entry>
    ...
    </entry>
    <entry>
        <id>http://stackoverflow.com/q/9439999</id>
        <re:rank scheme="http://stackoverflow.com">0</re:rank>
        <title type="text">Where is my data file?</title>
        <category scheme="http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest/tags" term="android"/>
        <category scheme="http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest/tags" term="file"/>
        <author>
            <name>cliff2310</name>
            <uri>http://stackoverflow.com/users/1128925</uri>
        </author>
        <link rel="alternate" href="http://stackoverflow.com/questions/9439999/where-is-my-data-file" />
        <published>2012-02-25T00:30:54Z</published>
        <updated>2012-02-25T00:30:54Z</updated>
        <summary type="html">
            <p>I have an Application that requires a data file...</p>
        </summary>
    </entry>
    <entry>
    ...
    </entry>
...
</feed>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
示例APP中从entry中提出的数据包含title, link, summary等标签。

实例化解析器

接下来这一步就是实例化解析器并启动解析过程。在下面的代码段中,解析器被实例化并设置为不处理命名空间,并且将InputStream作为其输入来源。它通过调用nextTag()方法来启动解析过程,调用readFeed()方法来提取并处理APP所关心的数据。
public class StackOverflowXmlParser {
    // We don't use namespaces
    private static final String ns = null;

    public List parse(InputStream in) throws XmlPullParserException, IOException {
        try {
            XmlPullParser parser = Xml.newPullParser();
            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
            parser.setInput(in, null);
            parser.nextTag();
            return readFeed(parser);
        } finally {
            in.close();
        }
    }
 ... 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

读取源

readFeed()方法开始对源进行实际操作。它会寻找元素标签”entry”并将其作为递归处理的起点。如果标签不是entry,则跳过。一旦整个源完成了递归处理,那么readFeed()则会返回一个List,它内部包含了entry对应的数据及内嵌的数据成员。
private List readFeed(XmlPullParser parser) throws XmlPullParserException, IOException {
    List entries = new ArrayList();
    parser.require(XmlPullParser.START_TAG, ns, "feed");
    while (parser.next() != XmlPullParser.END_TAG) {
        if (parser.getEventType() != XmlPullParser.START_TAG) {
            continue;
        }
        String name = parser.getName();
        // Starts by looking for the entry tag
        if (name.equals("entry")) {
            entries.add(readEntry(parser));
        } else {
            skip(parser);
        }
    }  
    return entries;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

解析XML

XML的解析包含以下步骤:
1.像Analyze the Feed中所描述的那样,需要确定你所关注的标签。这个示例从entry及其内嵌标签title, link, 和 summary中提取数据。
2.创建以下方法:
  • 对每个标签创建一个read方法,用于读取你所关注的标签。 比如readEntry(), readTitle(), 等等等等。解析器会从输入流中读取标签。当它探测到标签名为entry, title, link或summary时,它就会调用该标签所对应的方法。否则会跳过该标签。
  • 每个提取数据的方法会将解析器移动至下一个标签。比如:
    • 对于title及summary标签,解析器会调用readText()方法。该方法通过parser.getText()方法提取相应标签所对应的数据。
    • 对于link标签,解析器首先检查该link是否是我们所关注的,然后才会通过parser.getAttributeValue()方法将link的值提取出。
    • 对于entry标签,解析器会调用readEntry()方法。这个方法会解析entry的内部标签,并返回一个含有数据成员title, link,及summary的对象Entry。
  • 辅助方法skip()是个递归方法。有关更多它的信息,请继续往下。
下面的代码展示了如何解析部分entry,title,link及summary标签。
public static class Entry {
    public final String title;
    public final String link;
    public final String summary;
    private Entry(String title, String summary, String link) {
        this.title = title;
        this.summary = summary;
        this.link = link;
    }
}

// Parses the contents of an entry. If it encounters a title, summary, or link tag, hands them off
// to their respective "read" methods for processing. Otherwise, skips the tag.
private Entry readEntry(XmlPullParser parser) throws XmlPullParserException, IOException {
    parser.require(XmlPullParser.START_TAG, ns, "entry");
    String title = null;
    String summary = null;
    String link = null;
    while (parser.next() != XmlPullParser.END_TAG) {
        if (parser.getEventType() != XmlPullParser.START_TAG) {
            continue;
        }
        String name = parser.getName();
        if (name.equals("title")) {
            title = readTitle(parser);
        } else if (name.equals("summary")) {
            summary = readSummary(parser);
        } else if (name.equals("link")) {
            link = readLink(parser);
        } else {
            skip(parser);
        }
    }
    return new Entry(title, summary, link);
}
// Processes title tags in the feed.
private String readTitle(XmlPullParser parser) throws IOException, XmlPullParserException {
    parser.require(XmlPullParser.START_TAG, ns, "title");
    String title = readText(parser);
    parser.require(XmlPullParser.END_TAG, ns, "title");
    return title;
}

// Processes link tags in the feed.
private String readLink(XmlPullParser parser) throws IOException, XmlPullParserException {
    String link = "";
    parser.require(XmlPullParser.START_TAG, ns, "link");
    String tag = parser.getName();
    String relType = parser.getAttributeValue(null, "rel");  
    if (tag.equals("link")) {
        if (relType.equals("alternate")){
            link = parser.getAttributeValue(null, "href");
            parser.nextTag();
        } 
    }
    parser.require(XmlPullParser.END_TAG, ns, "link");
    return link;
}
// Processes summary tags in the feed.
private String readSummary(XmlPullParser parser) throws IOException, XmlPullParserException {
    parser.require(XmlPullParser.START_TAG, ns, "summary");
    String summary = readText(parser);
    parser.require(XmlPullParser.END_TAG, ns, "summary");
    return summary;
}
// For the tags title and summary, extracts their text values.
private String readText(XmlPullParser parser) throws IOException, XmlPullParserException {
    String result = "";
    if (parser.next() == XmlPullParser.TEXT) {
        result = parser.getText();
        parser.nextTag();
    }
    return result;
}
  ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76

跳过不关心的标签

解析XML数据的其中一个步骤就是需要忽略那些不需要关注的标签。下面是解析器的skip()方法。
private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
    if (parser.getEventType() != XmlPullParser.START_TAG) {
        throw new IllegalStateException();
    }
    int depth = 1;
    while (depth != 0) {
        switch (parser.next()) {
        case XmlPullParser.END_TAG:
            depth--;
            break;
        case XmlPullParser.START_TAG:
            depth++;
            break;
        }
    }
 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
它的工作过程如下:
  • 如果当前的项目不是START_TAG的话则抛出异常。
  • 它会消费掉START_TAG标签,直到遇到与之相匹配的END_TAG时结束。
  • 为了确保在正确的END_TAG时结束,这里使用了深度追踪的方式。
所以如果当前的标签含有内嵌标签,depth的值就不会是0,直到解析器消费完了改标签内的所有项目。试想一下解析器如何跳过<author>标签,它含有两个内嵌标签<name>和<uri>:
  • 第一次while循环,解析器所遇到的<author>标签的下一个标签是<name>的START_TAG项目,这时depth的值被自增到2.
  • 第二次while循环,解析器所遇到的下一个标签是</name>的END_TAG项目。这时depth的值被自减到1.
  • 第三次while循环,解析器所遇到的下一个标签是<uri>的START_TAG项目。这时depth的值被自增到2.
  • 第四次while循环,解析器所遇到的下一个标签是</uri>的END_TAG项目。这时depth的值被自减到1.
  • 第四次while循环,解析器所遇到的下一个标签是</author>的END_TAG项目。这时depth的值被自减到0.这表示<author>标签被成功跳过。

消费XML数据

示例应用中将获取解析XML源的过程放在了AsyncTask中。
loadPage()方法执行了以下过程:
  • 以字符串的形式实例化了XML源的URL地址。
  • 如果用户设置了网络连接并且网络连接状况良好,则调用new DownloadXmlTask().execute(url)。这里实例化了一个新的 DownloadXmlTask 对象,并执行了 execute() 方法,该方法会下载并解析源,最后将返回一个字符串形式的用于显示在UI界面上的结果。
public class NetworkActivity extends Activity {
    public static final String WIFI = "Wi-Fi";
    public static final String ANY = "Any";
    private static final String URL = "http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest";

    // Whether there is a Wi-Fi connection.
    private static boolean wifiConnected = false; 
    // Whether there is a mobile connection.
    private static boolean mobileConnected = false;
    // Whether the display should be refreshed.
    public static boolean refreshDisplay = true; 
    public static String sPref = null;
    ...

    // Uses AsyncTask to download the XML feed from stackoverflow.com.
    public void loadPage() {  

        if((sPref.equals(ANY)) && (wifiConnected || mobileConnected)) {
            new DownloadXmlTask().execute(URL);
        }
        else if ((sPref.equals(WIFI)) && (wifiConnected)) {
            new DownloadXmlTask().execute(URL);
        } else {
            // show error
        }  
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
DownloadXmlTask实现了AsyncTask的下面一些方法:
  • doInBackground() 执行了loadXmlFromNetwork()方法,它将源的URL地址作为参数传递给loadXmlFromNetwork(),当loadXmlFromNetwork()处理完成之后,将处理后的字符串返回。
  • onPostExecute() 获得刚刚返回的字符串将其显示在UI界面上。
// Implementation of AsyncTask used to download XML feed from stackoverflow.com.
private class DownloadXmlTask extends AsyncTask<String, Void, String> {
    @Override
    protected String doInBackground(String... urls) {
        try {
            return loadXmlFromNetwork(urls[0]);
        } catch (IOException e) {
            return getResources().getString(R.string.connection_error);
        } catch (XmlPullParserException e) {
            return getResources().getString(R.string.xml_error);
        }
    }
    @Override
    protected void onPostExecute(String result) {  
        setContentView(R.layout.main);
        // Displays the HTML string in the UI via a WebView
        WebView myWebView = (WebView) findViewById(R.id.webview);
        myWebView.loadData(result, "text/html", null);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
下面是loadXmlFromNetwork()方法的实现,它执行了以下过程:
1.实例化了StackOverflowXmlParser。它还创建了一个List的变量及title, url, summary变量,这些变量用于持有从XML中读取的值。 
2.调用downloadUrl(),它用于获取源,并返回一个InputStream对象。 
3.使用StackOverflowXmlParser 来解析刚刚的InputStream对象。 StackOverflowXmlParser将提取出的数据放入到List中。 
4.处理List,将其中的数据与HTML标签整合。 
5.最后返回一个HTML形式的字符串,字符串用于显示在主界面上。
// Uploads XML from stackoverflow.com, parses it, and combines it with
// HTML markup. Returns HTML string.
private String loadXmlFromNetwork(String urlString) throws XmlPullParserException, IOException {
    InputStream stream = null;
    // Instantiate the parser
    StackOverflowXmlParser stackOverflowXmlParser = new StackOverflowXmlParser();
    List<Entry> entries = null;
    String title = null;
    String url = null;
    String summary = null;
    Calendar rightNow = Calendar.getInstance(); 
    DateFormat formatter = new SimpleDateFormat("MMM dd h:mmaa");

    // Checks whether the user set the preference to include summary text
    SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
    boolean pref = sharedPrefs.getBoolean("summaryPref", false);

    StringBuilder htmlString = new StringBuilder();
    htmlString.append("<h3>" + getResources().getString(R.string.page_title) + "</h3>");
    htmlString.append("<em>" + getResources().getString(R.string.updated) + " " + 
            formatter.format(rightNow.getTime()) + "</em>");

    try {
        stream = downloadUrl(urlString);        
        entries = stackOverflowXmlParser.parse(stream);
    // Makes sure that the InputStream is closed after the app is
    // finished using it.
    } finally {
        if (stream != null) {
            stream.close();
        } 
     }

    // StackOverflowXmlParser returns a List (called "entries") of Entry objects.
    // Each Entry object represents a single post in the XML feed.
    // This section processes the entries list to combine each entry with HTML markup.
    // Each entry is displayed in the UI as a link that optionally includes
    // a text summary.
    for (Entry entry : entries) {       
        htmlString.append("<p><a href='");
        htmlString.append(entry.link);
        htmlString.append("'>" + entry.title + "</a></p>");
        // If the user set the preference to include summary text,
        // adds it to the display.
        if (pref) {
            htmlString.append(entry.summary);
        }
    }
    return htmlString.toString();
}
// Given a string representation of a URL, sets up a connection and gets
// an input stream.
private InputStream downloadUrl(String urlString) throws IOException {
    URL url = new URL(urlString);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setReadTimeout(10000 /* milliseconds */);
    conn.setConnectTimeout(15000 /* milliseconds */);
    conn.setRequestMethod("GET");
    conn.setDoInput(true);
    // Starts the query
    conn.connect();
    return conn.getInputStream();
}