2
第2章编写BlackBerry Java应用程序
应用程序管理
编写一个例程
重用一般代码
使用命令行
使用蓝牙开发环境
使用Eclipse开发环境
编程指南
当设备启动时,VM加载应用程序管理器,它管理在设备上所有运行的程序。对于其他Java程序,应用程序管理器的功能类似操作系统事件的中心调度员一样。
提供用户界面的应用程序扩展了net.rim.device.api.ui.UiApplication类。这个类为应用程序提供方法来注册事件监听者,管理线程以及UI组件。
没有提供用户界面的应用程序扩展了net.rim.device.api.system.Application类。
应用程序开始于main()函数。当一个程序开始时,它的main()线程调用enterEventDispatcher()来开始处理事件。这个线程运行所有绘图以及事件处理的代码,以及登等待应用程序队列里地事件。
当应用程序管理器接收到一个事件时,它将这个事件拷贝到合适的队列里,这个队列可以允许应用程序管理器指挥消息到特定的程序中。例如,前台的应用程序仅接收用户输入的消息。
扩展UiApplication基类
每个提供用户接口的应用程序扩展了UiApplication基类,UiApplication类为应用程序定义了方法来建立一个事件线程,并且显示和维护Screen对象。
定义main()
在main()中,为应用程序创建一个新的对象。调用enterEventDispatcher()使应用程序进入事件线程并且开始处理消息。
publicstaticvoidmain(String[] args) {
HelloWorld theApp =newHelloWorld();
theApp.enterEventDispatcher();
}
为你的应用程序定义缺省的构造子。缺省的构造子调用UiApplication.pushScreen()以显示当应用程序启动时出现的屏幕。在本例中,屏幕使一个新的HelloWorldScreen实例,它在下节的代码中定义:
publicHelloWorld() {
pushScreen(newHelloWorldScreen());
}
定义main屏幕
为了定义应用程序UI的主屏幕,扩展MainScreen类。MainScreen类是Screen的子类,它实现了TrackwheelListener和KeyboardListener接口,这些接口接收和响应用户交互。如果你扩展Screen类或者其子类中的一个,你并不是必须实现TrackwheelListener和KeyboardListener接口。
你的类至少应该重写2个MainScreen的方法:缺省的构造子和onClose().
在这个例子中,构造子调用了MainScreen的构造子。缺省地,MainScreen提供下列特性:
由一个Close菜单项的缺省菜单。
当你点击Close或者按Escape时,缺省的是关闭动作。为了提供客户定制行为,例如显示一个对话框提示,当用户点击Close菜单项或者按Escape按钮,重写onClose().
一个RichTextField的实例,一个可以接收焦点的只读富文本域为了得到更多关于增加UI组件到屏幕中的信息,参看40页的“提供屏幕导航”
一个Select菜单项的上下文菜单?为了得到更多信息,参看60页的“创建定制的上下文菜单“
接下来的例子创建了一个屏幕,它包含了一个富文本域。当富文本域接收到焦点时,菜单保安一个Close菜单项和一个Select上下文菜单项。
例: HelloWorld.java
/**
*HelloWorld.java
*Copyright(C)2001-2005ResearchInMotionLimited.Allrightsreserved.
*/
packagecom.rim.samples.docs.helloworld;
importnet.rim.device.api.ui.*;
importnet.rim.device.api.ui.component.*;
importnet.rim.device.api.ui.container.*;
importnet.rim.device.api.system.*;
importcom.rim.samples.docs.resource.*;
publicclassHelloWorldextendsUiApplication {
publicstaticvoidmain(String[] args) {
HelloWorld theApp =newHelloWorld();
theApp.enterEventDispatcher();
}
publicHelloWorld() {
pushScreen(newHelloWorldScreen());
}
}
finalclassHelloWorldScreenextendsMainScreen {
publicHelloWorldScreen() {
super();
LabelField title =newLabelField(“HelloWorld Sample”, LabelField.ELLIPSIS
| LabelField.USE_ALL_WIDTH);
setTitle(title);
add(newRichTextField(“Hello World!”));
}
publicbooleanonClose() {
Dialog.alert(“Goodbye!”);
System.exit(0);
returntrue;
}
}
抽象基类可以使你跨越多个类实现和重用一般功能。每个应用程序可以扩展单个基类。在,加入基类到一个库项目中。为每个应用程序创建一个独立的项目,定义库项目的依赖。
本指南的例程扩展了BaseApp类,它实现下面的功能:
扩展UiApplication类
实现KeyListener和TrackwheelListener接口
定义变量,例如一般的菜单项
定义一个方法创建应用程序菜单。
为菜单选择定义一个方法
定义一个抽象方法退出程序
例: BaseApp.java
/*
* * BaseApp.java
* * Copyright (C) 2001-2005 Research In Motion Limited. All rights reserved.
* */
packagecom.rim.samples.docs.baseapp;
importnet.rim.device.api.i18n.*;
importnet.rim.device.api.system.*;
importnet.rim.device.api.ui.container.*;
importnet.rim.device.api.ui.*;
importnet.rim.device.api.ui.component.*;
importcom.rim.samples.docs.resource.*;
publicabstractclassBaseApp
extendsUiApplicationimplementsBaseAppResource, KeyListener, TrackwheelListener {
privateMenuItem _closeItem;
privatestaticResourceBundle _resources =
ResourceBundle.getBundle(BUNDLE_ID, BUNDLE_NAME);
/* Constructor for the abstract base class. */
publicBaseApp() {
_closeItem =newMenuItem(_resources, MENUITEM_CLOSE, 200000, 10) {
publicvoidrun() {
onExit();
System.exit(0);
}
};
}
/* Override this method to add custom menu items. */
protectedvoidmakeMenu( Menu menu,intinstance) {
Field focus = UiApplication.getUiApplication().
getActiveScreen().getLeafFieldWithFocus();
if(focus !=null) {
ContextMenu contextMenu = focus.getContextMenu();
if( !contextMenu.isEmpty()) {
menu.add(contextMenu);
menu.addSeparator();
}
}
menu.add(_closeItem);
}
/* Invoked when the trackwheel is clicked. */
publicbooleantrackwheelClick(intstatus,inttime ) {
Menu menu =newMenu();
makeMenu( menu, 0);
menu.show();
returntrue;
}
/* Invoked when the trackwheel is released. */
publicbooleantrackwheelUnclick(intstatus,inttime ) {
returnfalse;
}
/* Invoked when the trackwheel is rolled. */
publicbooleantrackwheelRoll(intamount,intstatus,inttime) {
returnfalse;
}
publicbooleankeyChar(charkey,intstatus,inttime) {
/* Intercept the ESC key and exit the application. */
booleanretval =false;
switch(key) {
caseCharacters.ESCAPE:
onExit();
System.exit(0);
retval =true;
break;
}
returnretval;
}
/* Implementation of KeyListener.keyDown(). */
publicbooleankeyDown(intkeycode,inttime) {
returnfalse;
}
/* Implementation of KeyListener.keyRepeat(). */
publicbooleankeyRepeat(intkeycode,inttime) {
returnfalse;
}
/* Implementation of KeyListener.keyStatus(). */
publicbooleankeyStatus(intkeycode,inttime) {
returnfalse;
}
/* Implementation of KeyListener.keyUp(). */
publicbooleankeyUp(intkeycode,inttime) {
returnfalse;
}
protectedabstractvoidonExit();
}
为了编写,调试和编译应用程序,使用它是的一部分。
注:版本4.1使用了Sun JDK 5.0
1.在,选择File菜单,点击New Workspace
2.在Workspace name域,输入一个没有文件扩展名的名字。
3.在Create in this directory域,输入一个文档。
4.点击OK.
注:在包含工作空间的文件夹下的子目录中创建工程。
1.在的Project菜单,点击Create New Project.
2.在Project name域,输入一个没有文件扩张名的项目名称。
3.在Create project in this directory域,输入在此文件夹下创建项目的文件夹名称。
4.点击OK。
5.在工作空间文件区域里,双击项目名称来设置项目属性。
为了得到更多关于项目属性的信息,参看BlackBerry Integrated Development Environment Online Help。
注:保存源文件到和项目文件相同的文件夹下。和所有Java程序一样,为符合你的类使用的包层次关系的源代码创建文件结构,
1.在的File菜单,点击New。
2.在Source file name域,输入一个带.java文件扩展的文件名。
3.在Create source file in this directory域,输入文件夹名。
4.点击OK。
5.在编辑器区域,右击文件,然后点击Insert into project.
6.选择一个项目。
7.点击OK。
和集成源文件管理工具
你可以通过不同的源文件控制程序来使用。允许你为源文件控制程序设置“check out”, “add new file”和“revert”选项。在你为某一特定的源文件控制程序配置好选项后,可以自动check out文件,运行预先设置的命令,恢复改变,以及增加新创建的文件到源文件控制程序里。
1.在的File菜单,点击Edit->Preferences.
2.点击Source Control标签。
3.点击Check out标签。
4.在Check out域,输入命令以能打开一个文件来编辑。例如输入:
p4 edit %1
注:%1参数代表了名称和一个文件的绝对路径。例如:对于一个在c:\mypath的foo.java文件,当执行命令checkout %1,时,实际上运行命令checkout c:\mypath\foo.java。
5.点击Add file标签。
6.在Add new file域,输入命令以增加一个新的文件到源文件控制程序中。例如输入:
p4 add %1
7.点击 Revert file标签.
8.在Revert changes域,输入命令撤销一个源文件控制程序中的文件。例如输入:
p4 revert %1
9.-->单击OK。
当你编译项目时,编译你的源文件到Java字节编码(Bytecode),进行预验证,然后将类打包为一个.cod文件。
注:在Java ME中,字节编码验证分为2个步骤。已经编译的代码在它加载到设备之前预先验证,因此类加载时设备仅必须进行基本的验证。在编译项目时自动进行预验证。
当你编译一个项目时,如果需要,也编译项目依赖的任何库。
动作
过程
附加信息
编译所有项目
>在Build菜单,点击Build All
为了排除一个项目,设置项目属性。
编译所有活动的项目
>在Build菜单,点击Build。
在工作空间,活动的项目名是加粗的。为了改变哪些项目是活动的,在Project菜单,点击Set Active Projects.
编译单一项目
选择一个项目
在Build菜单,点击Build Selected。
―――
创建一个工作空间makefile
>在Build菜单,点击Generate Makefile and Resource.
―――
缺省的,已经编译的.cod文件使用项目名。为了改变这个名字,双击项目文件,点击Build标签,输入output file name(输出文件名)。
混淆应用程序
和传统的编译器不一样,平台的编译器因为受限的无线环境而优化了。在这里无线环境的目标是最小化应用程序的大小。作为结果的.cod文件提供了大量的类似于其他真正混淆包的混淆服务,以致能减小.cod文件本身的大小。例如,下面的信息将从.cod文件中移除:
所有调试的信息
本地变量名
源代码的行数
私有方法和成员名
同样,RIM相信没有必要为应用程序提供混淆,除了已经存在的平台编译的所有应用程序缺省提供的混淆。事实上,RIM没有对自己的产品进行任何额外的混淆。
对通过第三方工具混淆的支持没有集成到同样,需要一个命令行过程来混淆.cod文件供设备上使用。
混淆一个.cod文件
1.在,创建应用程序
提示:在这个过程中将项目文件放到一个独立的目录中。
2.创建临时的目录
3.将创建的jar文件拷贝到一个临时目录。
4.释放.jar文件的内容到一个临时目录。例如,在命令窗口输入下面的命令:*jar xvf SampleApplication.jar
5.删除释放为.jar文件部分的.cod文件。
6.删除.jar文件
7.混淆在临时目录下包含的类文件。
8.使用下面的命令对临时目录的内容运行预验证工具:
*preverify.exe -verbose -d . -classpath ..\lib\net_rim_api.jar;
9.在已混淆(和预验证)的类文件上运行rapc来创建一个.cod文件。使用下面的命令:
*rapc.exe -verbose import=..\lib\net_rim_api.jar listing=SampleApplication.lst codename=SampleApplication SampleApplication.rapc C:\yourTempDir\SampleApplication.class
生成API文档
使用一个宏给代码加入注释。
一旦启用这个功能,如果你在一个函数声明前的任何一行输入/**,生成下面的注释:
/**
* .
* @param menu.
* @param instance .
* @return.
*/
如果你在其他行输入/**, the BlackBerry IDE生成下面的注释::
/**
* .
*/
预先加载.作为查询字符串,再查询第一个实例,然后选择这个实例,这个特性允许你输入描述,然后按F3转移到后面的参数。
因为javadocs宏依赖于分解浏览的信息,仅在成功完成一个编译后增加javadocs。如果你的文件包含一个语法错误,并且在上面你正试着插入注释,宏不会得到函数声明。
增加一个新的编辑器宏
1.在Edit菜单,点击Preferences。
2.点击Editor标签。
3.点击Macros按钮。
4.从When I type下拉列表中,选择/**.
5.在Replace it with文本框里,输入@javadoc.
6.在同一行或者每个函数声明的前一行输入/**。例如,在下面的代码段,在单词”protected”开头的插入点输入/**.
/** protected int makeMenu(Menu menu, int instance) { ... }
包含一个命令行编译器RAPC。RAPC编译.java和.jar文件到可以运行在设备模拟器或者加载到设备上的.cod文件。
Rapc.exe在安装目录下的Bin下面。
RAPC接收下面的命令行选项。
选项
描述
import
指明RIM API和其他依赖的库
codename
指明应用程序名(这典型是.jar文件名)
midlet
指明应用程序是否是MIDlet
jad
指明JAD文件名
\filename_1.java[附加.java文件如果需要>]
指明.java文件名,如果正在编译java文件。
\JAR_filename.jar
指明.jar文件名,如果正在编译一个,jar文件。
为了利用狼牙开发环境使用设备模拟器,你需要从CSR(参看http://www.btdesigner.com/devcasira.htm)得到普通开发系统。
2.在主菜单,选择Edit>Preferences.
3.选择Simulator标签。
4.选择Ports标签。
5.在Communication port type域,选择合适的端口类型(参看Casira Endpoint文档)。
6.在Serial Port域,输入端口信息。
7.点击OK。
Java调试有线协议(Java Debug Wire Protocol, JDWP)的程序为模拟器提供一个接口。当你启动JDWP时,你可以使用第三方集成的开发环境。
启动JDWP
>点击开始>程序>Research In Motion>JDWP.
注:在启动JDWP之前,你必须从启动设备模拟器至少一次。你仅需要启动JDWP一次。为了启动一个设备模拟器,在Eclipse开发环境中,点击Run>Debug.
连接Eclipse开发环境
注:在完成本节的任务之前,安装和配置Eclipse开发环境。
完成下面的步骤:
1.扩展Sun JRE。
2.加入API文档。
3.设置Builder。
4.设置项目变量。
扩展Sun JRE
1.建立你的工作空间和项目。
2.启动Eclipse平台。
3.在Eclipse任务栏,点击Window>Preferences.
4.扩展Java项。
5.选择Installed JREs。
6.点击Add。
7.在Add JRE的窗口的JRE type域,选择Standard VM。
8.在JRE name域,为JRE输入一个名字。
9.在JRE home directory域,输入Sun JRE的位置。例如:
C:\Java\jdk1.5.0_02\jre.
10.务必使Use default system libraries域没有选。
11.点击Add External JARs。
12.浏览你的安装目录下的lib目录,例如:
C:\Program Files\Research In Motion\BlackBerry JDE 4.1.0\lib
13.选择net_rim_api.jar.
14.点击Open。
加入API文档
1.加入RIM.jar到你的项目。
2.在Add JRE窗口,扩展net_rim_api.jar文件。
3.选择Javadoc location.
4.点击Edit。
5.点击Browser。
6.找到找到你的目录下的docs\api目录。例如:
C:\Program Files\Research In Motion\BlackBerry JDE 4.1.0\docs\api
7.点击OK。
8.点击OK。
9.在Installed JREs窗口,选择新创建的JRE,缺省的是RIM JVM。
10.在Add JRE窗口,点击OK。
设置Builder
1.在Eclipse任务栏,点击Project>Properties.
2.选择Builders。
3.点击New。
4.在Choose configuration type窗口,选择Program.
5.点击OK。
6.在New_Builder窗口属性的Name域,为你的Builder输入一个名字。
7.在Location域,点击Browser File System。
8.找到你的目录下的Bin目录,例如:
C:\Program Files\Research In Motion\BlackBerry JDE 4.1.0\bin
9.选择rapc.exe文件。
10.点击Open。
设置项目变量
1.在Working Directory域,点击Variables。
2.在Select Variable窗口,选择build project.
3.点击OK。
4.在Arguments域,输入:
-quiet [desired space separated java, class, jar, or jad files] import=”C\Program Files\Research In Motion\BlackBerry JDE 4.1.0\lib\net_rim_api.jar” codename=C:\Development\ProjectName
例如:
-quiet C:\Development\TestProject\*.java import=”C:\Program Files\Research In Motion\BlackBerry JDE 4.1.0\lib\net_rim_api.jar” codename=C:\Development\TestProject.
5.点击OK。
6.在New_Builder窗口属性里,点击Build Options标签。
7.在Run the builder部分,验证下面的选项是否选择了。
After a“Clean”
During manual builds
During auto builds
8.点击OK。
9.在属性窗口,点击OK。
注:RAPC不支持通配符,如果输入的路径发生错误,使用空格分隔文件列。例如将C:\Development\TestProject\*.java代替为
C:\Development\A.java C:\Development\B.java.
当在Eclipse开发环境里调试时,为了阻止连接超时,为正在调试的程序设置超时连接值。
1.在Eclipse的任务栏,点击Windows>Preferences.
2.扩展Java项。
3.选择Debug。
4.在Communication部分的Debugger timeout域,输入值。
5.在Launch timeout域输入值。
注:你在Debugger timeout和Launch timeout设置的值依赖你的计算机处理速度。如果设置这些域之后连接问题继续出现,增加超时的值。
使用Eclipse开发环境进行调试
1.在Eclipse点击Run>Debug.
2.选择Remote Java Application.
3.点击New。
4.点击Source标签。
5.确认你的程序是否列出。
6.点击Close。
7.打开JDWP程序,为了得到更多信息,参看27页的“启动JDWP“。
8.在Eclipse任务栏,点击Run>Debug.
9.在Remote Java Application项下面,选择一个应用程序。
10.点击Debug。
注:如果出现下面的错误信息:“Failed to connect to remote VM. Connection timed out”,增加调试超时时间。为得到更多信息参看29页的”设置连接时间”.
使用本地变量
不管什么时候,尽量使用本地变量。访问本地变量比访问类的成员高效。
使用速记判断Boolean条件
为了代替第一个例子中没有必要的判断Boolean条件,使用第二个例子中的速记。最后编译的代码会更短:
if( boolean_expression ==true) {
returntrue;
}
else{
returnfalse;
}
// Do this
return( boolean_expression );
使类为final
当你创建一个代码库时,如果你知道他们永远不会被扩展,那么将他们标记为final。final关键字的出现允许编译器生成更高效的代码。
注:缺省,编译器标记你应用程序.cod文件中不会扩展的类为final。
使用int代替long
在Java中,一个long代表的是64位的整数。因为设备使用的是一个32位的处理器,如果你是用int代替long,操作将会快2-4倍。
避免垃圾回收
避免调用System.gc()进行垃圾回收。这个操作会占用许多时间,特别是在内存受限的设备上。让虚拟机进行垃圾回收。
对字符串使用静态变量
当定义String类型的静态字段(也成类字段),可以用静态变量(非final)代替常量(final)加快程序速度。反之,对于原始数据类型,例如int,也成立。
例如,你可能创建一个如下的String对象:
privatestaticfinalString x ="example";
对于这个静态常量(由final关键字标识),你使用常量的每个时候都会创建一个临时的String对象。在字节代码中,编译器去掉”x”,代替它的是字符串“example”,以致每次引用”x”时VM都会进行一次哈希表查询。
相比之下,度于静态变量(非final关键字),字符串只创建一次。仅当初始化“x”时,VM才进行哈希表查询。
注:你可以使用公共常量(也就是final字段),但是标记变量为私有。
避免String(String)的构造子
避免使用java.lang.String(String)构造子,因为它创建了一个没有必要的String对象,这个对象是作为参数提供的一个字符串的拷贝。因为String对象创建后不可以修改,所以拷贝典型没有必要。
注:当使用字符串构造子时,编译器会由警告。
String str =newString("abc");//避免.
String str =newString("found "+ n +" items");//避免.
在Java程序里,每个引用的字符串都作为一个java.lang.String类的对象。换言之,你可以编写如下面的代码来创建一个String。
String str ="abc";//建议.
String str ="found "+ n +" items";//建议.
编写有效的循环
在一个循环外考虑循环不变的代码。
//避免
for(inti = 0; i < vector.size(); i++ ) {
...
}
在这个实现中,在每次的迭代中vector.size()被调用,这是低效的。如果你的容器可能不止一个元素,将容器的大小赋值给本地变量。下面的代码移除了循环不变的代码:
//建议
intsize = vector.size();
for(inti = 0; i < size; ++i ) {
...
}
另外,如果你迭代的项的顺序并不重要,你可以向后迭代来避免栈上多余的本地变量,并且可以使比较更加快速。
for(inti = vector.size() - 1; i >= 0; --i ) {
...
}
优化子表达式
假如你使用相同的表达式2次,不要依赖编译器为你优化。使用本地变量,如下代码:
one( i+1 ); two( i+1 );// Avoid.
inttmp = i+1; one( tmp ); two( tmp );// Prefer.
优化除法操作
除法操作在设备上可能慢,因为处理器没有硬件触发指令。
在你的代码中,当一个正数除以2时,使用向右移一位(>>1)代替.仅当你知道你正在处理的正数时使用“向右移位”(>>).
midpoint = width / 2;// Avoid.
int= width >> 1;// Prefer.
避免java.util.Enumeration
避免使用java.util.Enumeration对象,除非你想隐藏数据(换句话说,为了返回一个数据的枚举代替数据本身。
// Avoid.
for(Enumeration e = v.elements(); e.hasMoreElements();) {
o = e.nextElement();
...
}
为一个Enumeration对象请求一个向量或者哈希表速度慢,并且创建了不必要的垃圾。代替它的是,迭代元素本身,如下面的例子:
// Prefer.
for(inti = v.size() - 1; i >=0; --i ) {
o = v.elementAt( i );
...
}
如果向量可能被其他线程修改,同步迭代,如下例子所示:
synchronized( v ) {
for(inti = v.size() - 1; i >=0; --i ) {
o = v.elementAt( i );
...
}
}
注:Java SE使用一个Iterator对象实现类似的功能,但是iterator在Java ME不可用。
使用instanceof进行转型
使用instanceof代替捕捉一个ClassCastException异常来判断转型是否成功。
// Avoid.
try
{
(String)x.whatever();
}
catch( ClassCastException e ) {
...
}
// Prefer.
if( xinstanceofString ) {
(String)x.whatever();
}
else{
...
}
使用instanceof比用try/catch要快。当转型失败发生异常时才使用try/catch。
在紧跟由一个instanceof检查的条件语句的第一个代码块里,编译器和虚拟机被优化为仅对一个类检查。在由一个instanceof检查的条件语句后面的转型利用了这个优化。
例如,编译器可以优化第一个例子,但是第二个不能:
// Prefer.
if( ainstanceof ) {
instance = ()a;
x.method(instance);
instance.method(x, y, z);
}
// Avoid.
if( ainstanceof ) {
x.method( ()a );
}
使用instanceof判断条件
为了编写较小而快的代码,如果使用instanceof判断条件,不要显式判断一个变量是否为null。当”e”为null时,表达式e instanceof 判断为false。
// Avoid.
if( e !=null&& einstanceofExampleClass ) {
if( e ==null|| ! ( einstanceofExampleClass)
// Prefer.
if( einstanceofExampleClass ) { ... }
if( ! ( einstanceofExampleClass ) ) { ... }
避免使用StringBuffer.append(StringBuffer)
CLDC不包含StringBuilder.append(StringBuilder)方法。采用将一个string buffer加到另一个的方法会创建一个String的中间对象。代替它的是,应用程序可以使用net.rim.device.api.util.StringUtilities.append( StringBuffer dst, StringBuffer src[, int offset, int length ] ).
// Avoid.
publicsynchronizedStringBuffer append(Object obj) {
returnappend(String.valueOf(obj));
}
// Prefer.
publicsynchronizedStringBuffer append(Object obj) {
if(objinstanceofStringBuffer) {
StringBuffer sb = (StringBuffer)obj;
net.rim.device.api.util.StringUtilities.append(this, sb, 0, sb )
returnthis;
}
returnappend(String.valueOf(obj));
}
当编写应用程序时可以采用下面的指南来减小编译后代码的大小。
设置适合的访问方式
当你创建代码库时,为字段和方法使用合适的访问权限可以显著减小编译后代码的大小。特殊的是,完成以下操作:
l不管什么时候,只要可能就将字段声明为private。除了好的编码实践外,这可以使编译器优化.cod文件。
l当可能时,使用缺省(包)的访问方式来代替public访问(也就是,忽略public和protected关键字)
避免创建接口
当创建API库时,避免创建接口,除非你预知API的多个实现。接口会产生更大更慢的代码。
使用内部的静态类
当创建一个内部的类隐藏一个在其他类里的类时,但是内部类没有引用外部类对象,声明这个内部类为static。这个操作压缩了对外部类引用的创建。
例如,下面的代码需要一个对外部类对象的引用。
// Avoid.
classouter {
inti;
classinner {
inner() {}
intexample() {returni; }
}
}
比较而言,下面的代码仅仅定义了内部类名的范围:
// Prefer.
classouter {
staticclassinner {
...
}
}
前一个例子是下面的缩写版本:
classouter {
...
}
classouter$inner {
...
}
当在内部类的方法里需要访问外部类数据时,仅仅使用一个非静态的内部类。如果为命名范围使用一个类,那么使这个类为static。
避免没有必要的初始化
在类里避免没有必要的字段初始化,这些类里,字段有缺省值。如果在一个类里没有初始化一个字段,它会自动使用下面的缺省值初始化字段。
l对象引用初始化为null
lint,byte或long初始化为0
lboolean初始化为false
例如,下面的代码段没有不同:
// Avoid.
classBadExample {
privateintfieldsCount= 0;// Avoid.
privateField _fieldWithFocus =null;// Avoid.
privateboolean_validLayout=false;// Avoid.
}
// Prefer.
classBetterExample {
privateintfieldsCount;// Prefer.
privateField _fieldWithFocus;// Prefer.
privateboolean_validLayout;// Prefer.
}
注:在一个方法里,必须显式初始化本地变量。
导入单独的类
一个应用程序仅使用了来自一个包的少量的类,这个程序应该导入单独的类,而不是整个库。
// Avoid.
importnet.rim.blackberry.api.browser.*;
// Prefer.
importnet.rim.blackberry.api.browser.Browser;
在对时间敏感的应用程序里,不要为任何事物依赖时间区域,除了显示本地时间给用户。
设备操作系统从January 1, 1970 (UTC)的午夜以毫秒来计算绝对时间。时间一般以CPU周期或毫秒来计量的。
系统时间区域改变
如果因为性能原因正在缓存对时间敏感的对象,那么记住设备上的系统时间区域可能会改变。
当时间区域改变时,系统会发送一个全局的事件消息给应用程序。GlobalEventListener的实现,包括eventOccurred(),会接受这个事件。利用invoking Application.addGlobalEventListener()注册你的实现。
publicvoideventOccurred(longguid,intdata0,intdata1, Object object0,Object object1 ) {
if( guid == DateTimeUtilities.GUID_TIMEZONE_CHANGED ) {
_cal.setTimeZone( TimeZone.getDefault() );
}
}
决定手持设备上的网络时间
调用RadioInfo.GetNetworkTime(long deviceTime)得到以毫秒计量的对应网络报告时间,然后调整本地时间。deviceTime参数代表现在的毫秒级时间。
使用多线程
有效的利用操作系统多线程的能力。特殊地,为网络连接或长操作(大于0.1秒)创建线程。为监听者使用背后(Background)线程,或者当程序启动时使用在背后运行地其他进程。
最小化内存地使用
为了最小化运行时内存,使用下面地指南:
使用原始类型(如int或Boolean)代替对象(如String或Integer)。
不要全部依赖垃圾回收。避免快速地创建多个对象。当完成使用他们时,将对象引用设置为null。尽可能重用对象。
将大地操作一到Server上,例如,在发送数据到设备之前,完成对数据地过滤或排序。
避免返回null
如果你正在编写一个公共地方法返回一个对象,仅在下面地条件下它可以返回一个null:
在正常地程序运行期间,null对象时期望的。
Javadoc @return参数描述了null是一个可能的返回值。
如果一个null返回值不是正常期望的,那么程序将抛出一个合适的异常强迫调用者显式的处理这个问题。调用者不期望检验一个null的返回值,除非文档说明了。
避免传递null给方法
不要传递一个null参数给API方法,除非API引用显式说明了方法支持他们。
小心传递null参数给构造子
当传递null参数给构造子时,为了避免混淆,将null转化为合适的对象:
new someObject ( (someObject)null );
如果一个类有两个或多个构造子,传递null参数可能不会唯一识别哪一个构造子将会使用。结果编译器会报错。在API参考里,并不是所有的构造子都会出现,因为有些构造子仅供内部使用。
通过转化null为合适的对象,你可以明确指明编译器会使用哪一个构造子。如果后续的API发行版本增加了新的构造子,它也可向前兼容。
使用long标记唯一标志符
使用一个long的标志符代替String标志符来标记唯一的常数,如GUID,哈希表键值,以及状态或上下文标志。
对于跨越第三方应用程序的标志符,为了保留其独立性,使用基于string生成的哈希生成的键值。在输入字符串里,包含了足够的信息提供唯一性。例如,使用一个完全信任的包名,如com.rim.samples.docs.helloworld。
转化一个string为long
1.在文本编辑器里,输入一个字符串。
2.选择字符串。
3.右击字符串。
4.选择Convert”String” to Long.
正确退出应用程序
在调用System.exit(int status)之前,你的程序应该完成任何清理,例如移除在运行时存储的程序不在需要的对象。
打印栈跟踪(Stack trace)
当VM发现代码使用catch(Exception e)捕获异常时,VM优化为排除栈跟踪。如果捕获到Throwable,它不会排除栈跟踪。
例如,下面的代码不会排除栈跟踪:
catch(IOException e) {
e.printStackTrace()
}
为了打印栈跟踪,编写类似下面的代码:
catch(Throwable t) {
t.printStackTrace();
}
当你调试时为了查看栈跟踪,捕获一个Throwable实例。
Last Updated:2007-01-05
Last Updated:2006-12-30
Last Updated:2006-03-24