TelnetConnection 实现 StreamConnection 接口的所有方法,只不过是调用已包装的 StreamConnection 和按需创建我们的自定义流。因为多次关闭 Connections 没有什么损害,我们还实现了 close() 来调用已包装的 StreamConnection 上的 close()。记住,在连接的输入和输出流全部关闭前,连接实际上没有关闭,所以您应该注意跟踪流并显式地关闭它们。您在关闭连接之前或之后关闭这些流没有区别。
请求 Connector 建立一个基于 socket:// 的连接会返回一个 StreamConnection,这也是您应该传递到 TelnetConnection 的内容。使用 telnet 时,好的实践是通过将 READ_WRITE 标志作为第二个可选的参数传递给 Connector.open(),告诉 Connector 您想要对其读写数据。即使您只想从流中读取数据,telnet 协商也将要求您将数据写回到连接。此外,您还应该指定第三个可选的参数,表明如果网络连接超时,也就是在某个时间间隔内没有收到响应时,让框架抛出异常。因为实现 MIDP 的移动设备的种类最多具有间歇的联网,您就需要得体地处理网络故障,获取任何的异常并通知用户连接已经断开。
Telnet Canvas
既然我们的网络基础设施已经就绪,我们需要提供一个用户界面。按照模块化的思想,这个用户界面将不对 telnet 连接做出假定或者根本不管网络连接是否存在。它将简单地接受字节并将其写到屏幕。
虽然我们在输入到来时可以使用 Form 并将 StringItems 或者甚至我们自己的 CustomItems 附加到 Form,但那也与应该使用 Form 的方法完全相反。此外,在多种 MIDP 设备上的 Form 的不同实现,意味着用户体验将有很大的变化,且在多数情况中将不会像我们所预期的那样工作。要对用户体验有完全的控制,包括能够调整我们的输出来适合屏幕的尺寸并指定所显示的字体,我们将创建自己的自定义 Canvas 子类。
使用我们的 TelnetCanvas 很容易:只要创建它、将其放到屏幕上并通过调用 receive() 为其传送 ASCII 字节。
...
TelnetCanvas canvas = new TelnetCanvas();
Display.getDisplay(this).setCurrent( canvas );
canvas.receive( "Hello World!\n" );
...
实现更有意思。让我们从 TelnetCanvas.java 中的构造函数开始:
public TelnetCanvas()
{
int width = getWidth();
int height = getHeight();
// get font and metrics
font = Font.getFont( Font.FACE_MONOSPACE,
Font.STYLE_PLAIN, Font.SIZE_SMALL );
fontHeight = (short) font.getHeight();
fontWidth = (short) font.stringWidth( "w" );
// calculate how many rows and columns we display
columns = (short) ( width / fontWidth );
rows = (short) ( height / fontHeight );
// divide extra space evenly around edges of screen
insetX = (short) ( ( width - columns*fontWidth ) / 2 );
insetY = (short) ( ( height - rows*fontHeight ) / 2 );
// initialize state: start with 4 screens of buffer
buffer = new byte[rows*columns*4];
cursor = 0;
...
}
除了初始化我们的变量外,我们要在运行时使自己适应于设备,就像所有好的 MIDlets 所应该的那样。终端依照传统都使用等宽字体,所以我们要求最小的字体并要测量高度和宽度,看看屏幕上可以显示多少字符。
我们想避免丢弃所接收的任何输入,所以在最初,我们创建了一个足够大的缓冲区,来保存 4 个屏幕的数据。这个尺寸是随意判断的;我们希望缓冲区足够小以适合内存,但又要足够大,使我们无需为较大的输入而需要经常重新分配。
对于 MIDlets 的可用内存量,不同制造商的不同设备间差别很大,所以您应该始终注意内存占用。因为我们显示 8 位的 ASCII 字符,所以使用 StringBuffer 甚至字符数组来存储内容都没有意义。为任意数值类型分配一个 int 的标准 Java 实践在 MIDP 世界中很多。一个 byte 数组就是所有我们所需的全部,它所占用的空间只是一个 char 数组的一半,是一个 int 数组的四分之一。
然而,不利的一面是我们需要手动地扩大数组和管理内存分配,这可是一件棘手的事情。无论何时我们接收到输入,我们要检查缓冲区是否要满了。如果是,就要尝试扩大缓冲区,如下面摘录所示:
public void receive( byte b )
{
...
// grow buffer as needed
if ( cursor + columns > buffer.length )
{
try
{
// expand by sixteen screenfuls at a time
byte[] tmp =
new byte[ buffer.length + rows*columns*16 ];
System.arraycopy(
buffer, 0, tmp, 0, buffer.length );
buffer = tmp;
}
catch ( OutOfMemoryError e )
{
// no more memory to grow:
// just clear half and reuse the existing buffer
System.err.println(
"Could not allocate buffer larger than: "
+ buffer.length );
int i, half = buffer.length / 2;
for ( i = 0; i < half; i++ )
buffer[i] = buffer[i+half];
for ( i = half; i < buffer.length; i++ )
buffer[i] = 0;
...
}
}
...
}
复制本页网址和标题,发送给你QQ/Msn的好友一起分享
上一篇:MIDP终端模拟之二:高级终端模拟
下一篇:J2EE入门教程之四