BlackBerry 应用程序开发者指南 第一卷:基础--第2章 编写BlackBerry Java应用程序
作者:Confach 发表于March 24,2006 18:05 pm
版权信息:可以任意转载, 转载时请务必以超链接形式标明文章原始出处和作者信息.
http://www.cnblogs.com/confach/articles/358028.html
2
第2章 编写BlackBerry Java应用程序
应用程序管理 编写一个例程 重用一般代码 使用BlackBerry IDE 使用命令行 使用蓝牙开发环境 使用Eclipse开发环境 编程指南 |
应用程序管理
当BlackBerry设备启动时,VM加载应用程序管理器,它管理在BlackBerry设备上所有运行的程序。对于其他Java程序,应用程序管理器的功能类似操作系统事件的中心调度员一样。
提供用户界面的应用程序扩展了net.rim.device.api.ui.UiApplication类。这个类为应用程序提供方法来注册事件监听者,管理线程以及UI组件。
没有提供用户界面的应用程序扩展了net.rim.device.api.system.Application类。
BlackBerry应用程序开始于main()函数。当一个程序开始时,它的main()线程调用enterEventDispatcher()来开始处理事件。这个线程运行所有绘图以及事件处理的代码,以及登等待应用程序队列里地事件。
当应用程序管理器接收到一个事件时,它将这个事件拷贝到合适的队列里,这个队列可以允许应用程序管理器指挥消息到特定的程序中。例如,前台的应用程序仅接收用户输入的消息。
编写一个例程
扩展UiApplication基类
每个提供用户接口的应用程序扩展了UiApplication基类,UiApplication类为应用程序定义了方法来建立一个事件线程,并且显示和维护Screen对象。
定义 main()
在main()中,为应用程序创建一个新的对象。调用enterEventDispatcher()使应用程序进入事件线程并且开始处理消息。
public static void main(String[] args) { HelloWorld theApp = new HelloWorld(); theApp.enterEventDispatcher();
} |
定义一个构造子
为你的应用程序定义缺省的构造子。缺省的构造子调用UiApplication.pushScreen()以显示当应用程序启动时出现的屏幕。在本例中,屏幕使一个新的HelloWorldScreen实例,它在下节的代码中定义:
public HelloWorld() { pushScreen(new HelloWorldScreen()); }
|
定义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-2005 Research In Motion Limited. All rights reserved.
*/
package com.rim.samples.docs.helloworld;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.system.*;
import com.rim.samples.docs.resource.*;
public class HelloWorld extends UiApplication {
public static void main(String[] args) {
HelloWorld theApp = new HelloWorld();
theApp.enterEventDispatcher();
}
public HelloWorld() {
pushScreen(new HelloWorldScreen());
}
}
final class HelloWorldScreen extends MainScreen {
public HelloWorldScreen() {
super();
LabelField title = new LabelField(“HelloWorld Sample”, LabelField.ELLIPSIS
| LabelField.USE_ALL_WIDTH);
setTitle(title);
add(new RichTextField(“Hello World!”));
}
public boolean onClose() {
Dialog.alert(“Goodbye!”);
System.exit(0);
return true;
}
}
重用一般代码
抽象基类可以使你跨越多个类实现和重用一般功能。每个应用程序可以扩展单个基类。在BlackBerry IDE,加入基类到一个库项目中。为每个应用程序创建一个独立的项目,定义库项目的依赖。
代码实例
本指南的例程扩展了BaseApp类,它实现下面的功能:
- 扩展UiApplication类
- 实现KeyListener和TrackwheelListener接口
- 定义变量,例如一般的菜单项
- 定义一个方法创建应用程序菜单。
- 为菜单选择定义一个方法
- 定义一个抽象方法退出程序
例: BaseApp.java
/*
* * BaseApp.java
* * Copyright (C) 2001-2005 Research In Motion Limited. All rights reserved.
* */
package com.rim.samples.docs.baseapp;
import net.rim.device.api.i18n.*;
import net.rim.device.api.system.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import com.rim.samples.docs.resource.*;
public abstract class BaseApp
extends UiApplication implements BaseAppResource, KeyListener, TrackwheelListener {
private MenuItem _closeItem;
private static ResourceBundle _resources =
ResourceBundle.getBundle(BUNDLE_ID, BUNDLE_NAME);
/* Constructor for the abstract base class. */
public BaseApp() {
_closeItem = new MenuItem(_resources, MENUITEM_CLOSE, 200000, 10) {
public void run() {
onExit();
System.exit(0);
}
};
}
/* Override this method to add custom menu items. */
protected void makeMenu( Menu menu, int instance) {
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. */
public boolean trackwheelClick( int status, int time ) {
Menu menu = new Menu();
makeMenu( menu, 0);
menu.show();
return true;
}
/* Invoked when the trackwheel is released. */
public boolean trackwheelUnclick( int status, int time ) {
return false;
}
/* Invoked when the trackwheel is rolled. */
public boolean trackwheelRoll(int amount, int status, int time) {
return false;
}
public boolean keyChar(char key, int status, int time) {
/* Intercept the ESC key and exit the application. */
boolean retval = false;
switch (key) {
case Characters.ESCAPE:
onExit();
System.exit(0);
retval = true;
break;
}
return retval;
}
/* Implementation of KeyListener.keyDown(). */
public boolean keyDown(int keycode, int time) {
return false;
}
/* Implementation of KeyListener.keyRepeat(). */
public boolean keyRepeat(int keycode, int time) {
return false;
}
/* Implementation of KeyListener.keyStatus(). */
public boolean keyStatus(int keycode, int time) {
return false;
}
/* Implementation of KeyListener.keyUp(). */
public boolean keyUp(int keycode, int time) {
return false;
}
protected abstract void onExit();
}
使用BlackBerry IDE
为了编写,调试和编译应用程序,使用BlackBerry IDE,它是BlackBerry JDE的一部分。
创建一个工作空间
1. 在BlackBerry IDE,选择File菜单,点击New Workspace
2. 在Workspace name域,输入一个没有文件扩展名的名字。
3. 在Create in this directory域,输入一个文档。
4. 点击OK.
创建一个项目
1. 在BlackBerry IDE的Project菜单,点击Create New Project.
2. 在Project name域,输入一个没有文件扩张名的项目名称。
3. 在Create project in this directory域,输入在此文件夹下创建项目的文件夹名称。
4. 点击OK。
5.在工作空间文件区域里,双击项目名称来设置项目属性。
为了得到更多关于项目属性的信息,参看BlackBerry Integrated Development Environment Online Help。
创建源文件
1. 在BlackBerry IDE的File菜单,点击New。
2. 在Source file name域,输入一个带.java文件扩展的文件名。
3. 在Create source file in this directory域,输入文件夹名。
4. 点击OK。
5. 在编辑器区域,右击文件,然后点击Insert into project.
6. 选择一个项目。
7. 点击OK。
和BlackBerry IDE集成源文件管理工具
你可以通过不同的源文件控制程序来使用BlackBerry IDE。BlackBerry IDE允许你为源文件控制程序设置“check out”, “add new file” 和“revert”选项。在你为某一特定的源文件控制程序配置好选项后,BlackBerry IDE可以自动check out文件,运行预先设置的命令,恢复改变,以及增加新创建的文件到源文件控制程序里。
1. 在BlackBerry IDE的File菜单,点击Edit->Preferences.
2. 点击Source Control 标签。
3. 点击Check out标签。
4. 在Check out域,输入命令以能打开一个文件来编辑。例如输入:
p4 edit %1
注:%1 参数代表了名称和一个文件的绝对路径。例如:对于一个在c:\mypath的foo.java文件,当BlackBerry IDE执行命令checkout %1,时,BlackBerry IDE实际上运行命令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。
编译项目
当你编译项目时,BlackBerry IDE编译你的源文件到Java 字节编码(Bytecode),进行预验证,然后将类打包为一个.cod文件。
注:在Java ME中,字节编码验证分为2个步骤。已经编译的代码在它加载到BlackBerry设备之前预先验证,因此类加载时BlackBerry 设备仅必须进行基本的验证。BlackBerry IDE在编译项目时自动进行预验证。
当你编译一个项目时,如果需要,BlackBerry也编译项目依赖的任何库。
动作 |
过程 |
附加信息 |
编译所有项目 |
> 在Build菜单,点击Build All |
为了排除一个项目,设置项目属性。 |
编译所有活动的项目 |
> 在Build菜单,点击Build。 |
在工作空间,活动的项目名是加粗的。为了改变哪些项目是活动的,在Project菜单,点击Set Active Projects. |
编译单一项目 |
|
――― |
创建一个工作空间makefile |
>在Build菜单,点击Generate Makefile and Resource. |
――― |
缺省的,已经编译的.cod文件使用项目名。为了改变这个名字,双击项目文件,点击Build标签,输入output file name(输出文件名)。
混淆应用程序
和传统的编译器不一样,BlackBerry平台的编译器因为受限的无线环境而优化了。在这里无线环境的目标是最小化应用程序的大小。作为结果的.cod文件提供了大量的类似于其他真正混淆包的混淆服务,以致能减小.cod文件本身的大小。例如,下面的信息将从.cod文件中移除:
- 所有调试的信息
- 本地变量名
- 源代码的行数
- 私有方法和成员名
同样,RIM相信没有必要为应用程序提供混淆,除了已经存在的BlackBerry平台编译的所有应用程序缺省提供的混淆。事实上,RIM没有对自己的产品进行任何额外的混淆。
对通过第三方工具混淆的支持没有集成到BlackBerry JDE .同样,需要一个命令行过程来混淆.cod文件供BlackBerry设备上使用。
混淆一个.cod文件
1. 在BlackBerry IDE,创建应用程序
<--[if gte vml 1]>
2. 创建临时的目录
3. 将BlackBerry IDE创建的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文档
使用一个BlackBerry IDE宏给代码加入注释。
一旦启用这个功能,如果你在一个函数声明前的任何一行输入/**,BlackBerry IDE生成下面的注释:
/**
* <description>.
* @param menu <description>.
* @param instance <description>.
* @return <description>.
*/
如果你在其他行输入/**, the BlackBerry IDE 生成下面的注释::
/**
* <description>.
*/
BlackBerry IDE预先加载<description>.作为查询字符串,再查询第一个实例,然后选择这个实例,这个特性允许你输入描述,然后按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) { ... }
使用命令行
BlackBerry JDE包含一个命令行编译器RAPC。RAPC编译.java和.jar文件到可以运行在BlackBerry设备模拟器或者加载到BlackBerry设备上的.cod文件。
Rapc.exe在BlackBerry JDE安装目录下的Bin下面。
RAPC接收下面的命令行选项。
选项 |
描述 |
import |
指明RIM API和其他依赖的库 |
codename |
指明应用程序名(这典型是.jar文件名) |
midlet |
指明应用程序是否是MIDlet |
jad |
指明JAD文件名 |
\filename_1.java[附加.java文件如果需要>] |
指明.java文件名,如果正在编译java文件。 |
\JAR_filename.jar |
指明.jar文件名,如果正在编译一个,jar文件。 |
使用蓝牙开发环境
为了利用狼牙开发环境使用BlackBerry设备模拟器,你需要从CSR(参看http://www.btdesigner.com/devcasira.htm)得到普通开发系统。
利用一个BlackBerry设备模拟器使用蓝牙开发环境
1. 打开BlackBerry IDE
2. 在主菜单,选择Edit>Preferences.
3. 选择Simulator标签。
4. 选择Ports标签。
5. 在Communication port type域,选择合适的端口类型(参看Casira Endpoint文档)。
6. 在Serial Port域,输入端口信息。
7. 点击OK。
使用Eclipse开发环境
Java调试有线协议(Java Debug Wire Protocol, JDWP)的程序为BlackBerry模拟器提供一个接口。当你启动JDWP时,你可以使用第三方集成的开发环境。
启动JDWP
>点击开始>程序>Research In Motion>BlackBerry JDE 4.1.0>JDWP.
注:在启动JDWP之前,你必须从BlackBerry IDE启动BlackBerry设备模拟器至少一次。你仅需要启动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. 浏览你的BlackBerry IDE安装目录下的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. 找到找到你的BlackBerry IDE目录下的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. 找到你的BlackBerry IDE目录下的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 ) { return true; } else { return false; } // Do this return( boolean_expression ); |
使类为final
当你创建一个代码库时,如果你知道他们永远不会被扩展,那么将他们标记为final。final关键字的出现允许编译器生成更高效的代码。
注:缺省,BlackBerry JDE编译器标记你应用程序.cod文件中不会扩展的类为final。
使用int代替long
在Java中,一个long代表的是64位的整数。因为BlackBerry设备使用的是一个32位的处理器,如果你是用int代替long,操作将会快2-4倍。
避免垃圾回收
避免调用System.gc()进行垃圾回收。这个操作会占用许多时间,特别是在内存受限的BlackBerry设备上。让虚拟机进行垃圾回收。
对字符串使用静态变量
当定义String类型的静态字段(也成类字段),可以用静态变量(非final)代替常量(final)加快程序速度。反之,对于原始数据类型,例如int,也成立。
例如,你可能创建一个如下的String对象:
private static final String x = "example"; |
对于这个静态常量(由final关键字标识),你使用常量的每个时候都会创建一个临时的String对象。在字节代码中,编译器去掉”x”,代替它的是字符串“example”,以致每次引用”x”时VM都会进行一次哈希表查询。
相比之下,度于静态变量(非final关键字),字符串只创建一次。仅当初始化“x”时,VM才进行哈希表查询。
注:你可以使用公共常量(也就是final字段),但是标记变量为私有。
避免String(String)的构造子
避免使用java.lang.String(String)构造子,因为它创建了一个没有必要的String对象,这个对象是作为参数提供的一个字符串的拷贝。因为String对象创建后不可以修改,所以拷贝典型没有必要。
注:当使用字符串构造子时,编译器会由警告。
String str = new String("abc"); // 避免. String str = new String("found " + n + " items"); // 避免. |
在Java程序里,每个引用的字符串都作为一个java.lang.String类的对象。换言之,你可以编写如下面的代码来创建一个String。
String str = "abc"; // 建议. String str = "found " + n + " items"; // 建议. |
编写有效的循环
在一个循环外考虑循环不变的代码。
//避免 for( int i = 0; i < vector.size(); i++ ) { ... } |
在这个实现中,在每次的迭代中vector.size()被调用,这是低效的。如果你的容器可能不止一个元素,将容器的大小赋值给本地变量。下面的代码移除了循环不变的代码:
// 建议 int size = vector.size(); for( int i = 0; i < size; ++i ) { ... } |
另外,如果你迭代的项的顺序并不重要,你可以向后迭代来避免栈上多余的本地变量,并且可以使比较更加快速。
for( int i = vector.size() - 1; i >= 0; --i ) { ... } |
优化子表达式
假如你使用相同的表达式2次,不要依赖编译器为你优化。使用本地变量,如下代码:
one( i+1 ); two( i+1 ); // Avoid. int tmp = i+1; one( tmp ); two( tmp ); // Prefer. |
优化除法操作
除法操作在BlackBerry设备上可能慢,因为处理器没有硬件触发指令。
在你的代码中,当一个正数除以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( int i = v.size() - 1; i >=0; --i ) { o = v.elementAt( i ); ... } |
如果向量可能被其他线程修改,同步迭代,如下例子所示:
synchronized( v ) { for( int i = 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( x instanceof String ) { (String)x.whatever(); } else { ... } |
使用instanceof比用try/catch要快。当转型失败发生异常时才使用try/catch。
在紧跟由一个instanceof检查的条件语句的第一个代码块里,BlackBerry IDE编译器和虚拟机被优化为仅对一个类检查。在由一个instanceof检查的条件语句后面的转型利用了这个优化。
例如,编译器可以优化第一个例子,但是第二个不能:
// Prefer. if ( a instanceof <type> ) { <type> instance = (<type>)a; x.method(instance); instance.method(x, y, z); } // Avoid. if( a instanceof <type> ) { x.method( (<type>)a ); } |
使用instanceof判断条件
为了编写较小而快的代码,如果使用instanceof判断条件,不要显式判断一个变量是否为null。当”e”为null时,表达式e instanceof <type>判断为false。
// Avoid. if( e != null && e instanceof ExampleClass ) { if( e == null || ! ( e instanceof ExampleClass) // Prefer. if( e instanceof ExampleClass ) { ... } if( ! ( e instanceof ExampleClass ) ) { ... } |
避免使用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. public synchronized StringBuffer append(Object obj) { return append(String.valueOf(obj)); } // Prefer. public synchronized StringBuffer append(Object obj) { if (obj instanceof StringBuffer) { StringBuffer sb = (StringBuffer)obj; net.rim.device.api.util.StringUtilities.append( this, sb, 0, sb ) return this; } return append(String.valueOf(obj)); } |
减小代码大小
当编写应用程序时可以采用下面的指南来减小编译后代码的大小。
设置适合的访问方式
当你创建代码库时,为字段和方法使用合适的访问权限可以显著减小编译后代码的大小。特殊的是,完成以下操作:
l 不管什么时候,只要可能就将字段声明为private。除了好的编码实践外,这可以使编译器优化.cod文件。
l 当可能时,使用缺省(包)的访问方式来代替public访问(也就是,忽略public和protected关键字)
避免创建接口
当创建API库时,避免创建接口,除非你预知API的多个实现。接口会产生更大更慢的代码。
使用内部的静态类
当创建一个内部的类隐藏一个在其他类里的类时,但是内部类没有引用外部类对象,声明这个内部类为static。这个操作压缩了对外部类引用的创建。
例如,下面的代码需要一个对外部类对象的引用。
// Avoid. class outer { int i; class inner { inner() {} int example() { return i; } } } |
比较而言,下面的代码仅仅定义了内部类名的范围:
// Prefer. class outer { static class inner { ... } } |
前一个例子是下面的缩写版本:
class outer { ... } class outer$inner { ... } |
当在内部类的方法里需要访问外部类数据时,仅仅使用一个非静态的内部类。如果为命名范围使用一个类,那么使这个类为static。
避免没有必要的初始化
在类里避免没有必要的字段初始化,这些类里,字段有缺省值。如果在一个类里没有初始化一个字段,它会自动使用下面的缺省值初始化字段。
l 对象引用初始化为null
l int,byte或long初始化为0
l boolean初始化为false
例如,下面的代码段没有不同:
// Avoid. class BadExample { private int fieldsCount = 0; // Avoid. private Field _fieldWithFocus = null; // Avoid. private boolean _validLayout = false; // Avoid. } // Prefer. class BetterExample { private int fieldsCount; // Prefer. private Field _fieldWithFocus; // Prefer. private boolean _validLayout; // Prefer. } |
注:在一个方法里,必须显式初始化本地变量。
导入单独的类
一个应用程序仅使用了来自一个包的少量的类,这个程序应该导入单独的类,而不是整个库。
// Avoid. import net.rim.blackberry.api.browser.*; // Prefer. import net.rim.blackberry.api.browser.Browser; |
在BlackBerry设备上使用时间
在对时间敏感的应用程序里,不要为任何事物依赖时间区域,除了显示本地时间给用户。
BlackBerry 设备钟
BlackBerry设备操作系统从January 1, 1970 (UTC)的午夜以毫秒来计算绝对时间。时间一般以CPU周期或毫秒来计量的。
系统时间区域改变
如果因为性能原因正在缓存对时间敏感的对象,那么记住BlackBerry设备上的系统时间区域可能会改变。
当时间区域改变时,系统会发送一个全局的事件消息给应用程序。GlobalEventListener的实现,包括eventOccurred(),会接受这个事件。利用invoking Application.addGlobalEventListener()注册你的实现。
public void eventOccurred( long guid, int data0, int data1, Object object0,Object object1 ) { if( guid == DateTimeUtilities.GUID_TIMEZONE_CHANGED ) { _cal.setTimeZone( TimeZone.getDefault() ); } } |
决定手持设备上的网络时间
调用RadioInfo.GetNetworkTime(long deviceTime)得到以毫秒计量的对应网络报告时间,然后调整本地时间。deviceTime参数代表现在的毫秒级时间。
建议的实践
使用多线程
有效的利用BlackBerry操作系统多线程的能力。特殊地,为网络连接或长操作(大于0.1秒)创建线程。为监听者使用背后(Background)线程,或者当程序启动时使用在背后运行地其他进程。
最小化内存地使用
为了最小化运行时内存,使用下面地指南:
- 使用原始类型(如int或Boolean)代替对象(如String或Integer)。
- 不要全部依赖垃圾回收。避免快速地创建多个对象。当完成使用他们时,将对象引用设置为null。尽可能重用对象。
- 将大地操作一到Server上,例如,在发送数据到BlackBerry设备之前,完成对数据地过滤或排序。
避免返回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. 在BlackBerry IDE文本编辑器里,输入一个字符串。
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