2005年11月15日

几个比较好的Symbian论坛

Symbian公司官方网站
http://www.symbian.com/

UIQ公司官方网站:
http://www.uiq.com/

UIQ开发者门户网站(含UIQ论坛):
http://developer.uiq.com/

诺基亚论坛:
http://www.forum.nokia.com/

诺基亚论坛讨论区:
http://discussion.forum.nokia.com/forum/

诺基亚论坛中文讨论区:
http://discussion.forum.nokia.com/forum/forumdisplay.php?s=2c69701ec253c19473de786fc91d0fdc&forumid=71

索爱官方网站:
http://www.sonyericsson.com/

索爱开发者论坛:
http://developer.sonyericsson.com/show_forums.do

NewLC:
http://www.newlc.com/

NewLC论坛:
http://forum.newlc.com/

My Symbian:
http://my-symbian.com/
http://my-symbian.com/forum/

All About Symbian:
http://www.allaboutsymbian.com/
http://www.allaboutsymbian.com/forum/

Symbian One:
http://www.symbianone.com/
http://www.symbianone.com/index.php?option=com_simpleboard&Itemid=48&Itemid=74

——————————————————————————–
WDA中文网
http://www.wda.com.cn/

CSDN:
http://www.csdn.net/
http://community.csdn.net/
http://www.csdn.net/mobile/
http://www.csdn.net/mobile/nokia/listncsp.aspx

移动未来:
http://www.move2008.com/
http://www.move2008.com/bbs/index.asp

Mobile2008(挂了):
http://www.mobile2008.com/

M9W移动无限:
http://www.m9w.com/
http://www.m9w.com/forum/

其它:
http://www.symbian.org.cn/
http://www.3g4g.net/
http://www.sf.org.cn/
http://www.msale.net/

——————————————————————————–
以下是热心网友推荐的,未验证:
http://www.3g-express.com/
http://www.ayychina.com/7650bbs/index.asp
http://www.ioicn.com/
http://www.ioicn.com.cn/
http://www.phonesky.net/
http://www.dfun.net/
http://www.ebds.com.cn/bbs/
http://www.bwo.com.cn/forum/

2005年11月10日

在小型设备上获取 Java 网络应用程序

Soma Ghosh
应用程序开发人员, Entigo
2002 年 12 月 09 日

如果您一直都在关注无线专区中 Soma Ghosh 的文章,那么您就已经知道了如何使用您的 Java 技能来为手持设备构建简单的应用程序。那您如何将那些设备连接到外部世界呢?在本文中,Ghosh 讨论了 J2ME 联网中至关重要的 javax.microedition.io 类和 java.io 类。您将学习到 J2ME 应用程序如何处理 URL 和接受输入,本文甚至还会带您完成一个样本程序,这个程序把货币兑换信息下载到网络可访问的、遵循 J2ME 的任意设备上。

J2ME I/O 与联网:概览
Java 2 平台,袖珍版(Java 2 Platform,Micro Edition (J2ME))提供了把网络上可用的资源扩展到移动空间中的联网功能。现在,在移动电话或掌上电脑获取实时股票报价或最新贷币汇率是可能的。

javax.microedition.io 中的各个类和接口处理移动信息设备框架(Mobile Information Device Profile,MIDP)的联网功能,MIDP 则是一个开发移动设备应用程序的平台。(想了解更多有关 MIDP 的信息,请访问下面的 参考资料部分,链接到我先前已发表在 developerWorks上的关于这个主题的文章。)

另一方面, java.io 包给 MIDP 提供了输入/输出(input/output(I/O))功能。它的各个类和接口为数据流提供了系统输入和输出。这个 J2ME 包是 Java 2 平台,标准版(Java 2 Platform,Standard Edition(J2SE)) java.io 包的一个子集,它处理低级别的数据 I/O。

J2ME 网络连接性最关键的方面是移动设备与 Web 服务器间的通信。这种通信本质上是客户机/服务器机制,其中移动设备充当 Web 客户机的角色并有能力与企业系统、数据库、公司内部网和因特网建立接口。

J2ME 联网活动可以按照通信协议分为许多种类别。我们将在以下几部分中依次讨论每一种类别。

低级别的 IP 联网
这一类别涉及到套接字、数据报、串口和文件 I/O 通信。基于套接字的通信遵循面向连接的 TCP/IP 协议。另一方面,基于数据报的通信遵循无连接的 UDP/IP 协议。UDP 为应用程序提供了不必建立连接就能发送经过封装的原始 IP 数据报的方法。面向连接的协议需要源地址和目的地址,与此不同,数据报只需要目的地址。下面是数据报连接用来在某端口接受数据报的一个 URI:


datagram://:1234

这里是数据报连接用来在某端口将数据报发送到服务器的一个 URI:


datagram://123.456.789.12:1234

低级别的 IP 联网还可以处理文件 I/O 并且能够允许 MIDlet 注册对本地串口进行网络访问。

安全联网
J2ME 中的安全联网涉及到一些为了与基于 Web 的网络服务进行安全通信而提供的额外接口。这些安全接口由 IP 网络上的 HTTPS 和 SSL/TLS 协议访问提供。

HTTP 联网
移动设备与 Web 服务器之间基于 HTTP(Hypertext Transfer Protocol,超文本传输协议)进行通信。HTTP 是一个面向连接的请求-响应(request-response)协议,在这个协议中,必须在发送请求之前设置请求的各参数。

图 1 说明了移动设备与 Web 服务器间的通信机制。

图 1. 移动设备与 Web 服务器间的连接机制
移动设备与 Web 服务器间的连接机制

连接框架
J2ME 联网旨在处理移动设备的广泛频谱不同的需要。同时,联网系统必须是特定于设备的。为了应付这些要求,J2ME 联网引入了 通用连接框架(generic connection framework)的概念。

通用连接框架的设想是以 Java 接口的形式定义一些能够覆盖联网和文件 I/O 的通用方面的抽象。这个体系结构广泛支持各种手持设备,而将这些接口的实际实现留给了各个设备制造商。设备制造商可以根据其设备的实际功能选择要在它的特定 MIDP 中实现哪个接口。

由 Java 接口定义的通用方面分为以下几种形式的基本通信类型:

  • 基本串行输入(由 javax.microedition.io.InputConnection 定义)
  • 基本串行输出(由 javax.microedition.io.OutputConnection 定义)
  • 数据报通信(由 javax.microedition.io.DatagramConnection 定义)
  • 用于客户机-服务器(client-server)通信的套接字通信通知机制(由 javax.microedition.io.StreamConnectionNotifier 定义)
  • 与 Web 服务器进行的基本 HTTP 通信(由 javax.microedition.io.HttpConnection 定义)

J2ME 中的 URL 处理
J2ME 中的 URL 处理涉及到从移动设备打开一个到 Web 服务器的连接并处理移动设备到 Web 服务器间的数据 I/O。这个过程发生在下面的阶段:

  • 建立(Setup),此时尚未建立到服务器的连接。移动设备准备一堆请求参数并准备接受和解释随后的响应。
  • 已连接(Connected),此时连接已经被建立,请求参数已经被发送并在期待响应。
  • 已关闭(Closed),此时连接已经被关闭。

J2ME 定义了 javax.microedition.io.Connector 类,这个类包含了用于创建所有连接对象的各个静态(static)方法。这一任务是通过根据平台名称和所请求连接的协议动态地查找一个类来完成的。

在 URL 处理中, Connector.open() 用来打开 URL;它返回一个 HttpConnection 对象。 Connector.open() 方法的字符串(string)参数是一个有效的 URL。URL 字符串由于通信协议的不同而不同,下面的清单 1 到清单 5 演示了这一点。

清单 1. 调用基于 HTTP 的通信


Connection conn = Connector.open("http://www.yahoo.com");


清单 2. 调用基于流的套接字通信


Connection conn = Connector.open("socket://localhost:9000");


清单 3. 调用基于数据报的套接字通信


Connection conn = Connector.open("datagram://:9000");


清单 4. 调用串口通信


Connection conn = Connector.open("comm:0;baudrate=9000");


清单 5. 调用文件 I/O 通信


Connection conn = Connector.open("file://myfile.dat");

Connector.open() 方法还可以接受访问模式(值为 READWRITEREAD_WRITE )以及一个用来表示调用者想要得到超时通知的标志。

在安全的联网中,当 https:// 连接字符串被访问时, Connector.open() 就会返回 HttpsConnection 。当 ssl:// 连接字符串被访问时, Connector.open() 就会返回 SecureConnection

无论使用哪一种类型的 URL,调用 Connector.open() 都会打开一个从 Connectionjava.io.InputStream 的字节输入流。这个方法用来读取文件的每一个字符,一直读到文件末尾(以 -1 为标志)。如果抛出一个异常,连接和流就会被关闭。

与此相似,为了进行输出,代表字节输出流的 java.io.OutputStream 将被从 Connection 打开。

InputStreamOutputStream 分别与 java.io.DataInputStreamjava.io.DataOutputStream 相对应。 DataInputStream 让应用程序用与机器无关的方式从底层输入流读取基本的 Java 数据类型。 java.io.DataOutputStream 让应用程序用可移植的方式把基本的 Java 数据类型写到输出流。

清单 6 说明了如何使用 HttpConnection 从 URL 输入数据。

清单 6. 使用 HttpConnection 从 URL 输入数据


String getViaHttpConnection(String url) throws IOException {
         HttpConnection c = null;
         InputStream is = null;
              StringBuffer str = new StringBuffer();

              try {
                  c = (HttpConnection)Connector.open(url);

                // Getting the InputStream will open the connection
                // and read the HTTP headers. They are stored until
                // requested.
                is = c.openInputStream();

                // Get the length and process the data
             int len = (int)c.getLength();
             int ch;
             while ((ch = is.read()) != -1) {
                 str.append((char)ch);
             }

          } finally {
          if (is != null)
              is.close();
          if (c != null)
              c.close();
        }

一个贷币兑换应用程序
我们将通过一个贷币兑换应用程序来说明迄今为止所概述过的概念,这个应用程序将会显示美元(U.S. dollar,USD)和英镑(British pound,GBP)之间最新的汇率。这个应用程序还会显示任意与当前日期和当前时间相关的信息。

这个应用程序的 UI 由一个表单( Form )和一个退出(exit)命令组成,该表单嵌入了代表一个只显示字符串的 StringItem ,退出命令用于完成调用时让应用程序退出。

一旦启动应用程序,URL 请求便已准备就绪。基本的货币符号被提供给请求。接下来,我们需要打开一个移动设备与 Web 服务器间的 URL 连接。打开一个 HttpConnection 并为数据输入建立一个 InputStream 。所获得的数据是一个字符流,这个多字符流附加在 String 中。产生的 String 代表 HTML 输出。由于我们的移动设备上的浏览器不能显示 HTML,因而我们将解析 HTML String 来获取货币值以及任何相关的信息。

我们的程序将在 HTML String 中搜索一个特定模式,即 USDGBP 。一旦确定了这个模式的位置,搜索便查找十进制值。当获得了小数点的位置后,各个数字值便会被检索并以适当的顺序排列,从而获取货币值。清单 7 说明了如何获取货币值。

清单 7. 检索货币值


            String retVal = "";
            int dec = 0;

            int index = str.indexOf("USDGBP");
            if (index != -1)
                   str = str.substring(index, str.length());

            if ( (( dec = str.indexOf(".")) != -1) && (!(str.endsWith(".")))
             && Character.isDigit(str.charAt(dec+1)) )
{
  String front = "";
  int find = dec-1;
  while (Character.isDigit(str.charAt(find)))
  {
    front += str.charAt(find);
    find--;
    }
  retVal += new StringBuffer(front).reverse().toString();
  retVal += ".";

  String back = "";
  int bind = dec+4;
  while (Character.isDigit(str.charAt(bind)))
    {
     back += str.charAt(bind);
     bind--;
    }
  retVal += new StringBuffer(back).reverse().toString();

}

相关信息也是通过查找某些特定的字符串模式来获取的。一旦确定了数据在 HTML String 中的位置,一个偏移量便会被应用来获取日期和时间。这个信息被与原先用于搜索的字符串模式附加在一起。清单 8 说明了如何获取相关信息。

清单 8. 检索相关信息


// Get the time of Currency Exchange and Related information
  int timeIndex = str.indexOf("U.S. Markets Closed.");
  String retTime = "";
  if (timeIndex != -1)
  {
   retTime = str.substring(timeIndex-34, timeIndex);
   retTime += " U.S. Markets Closed ";
  }

清单 9 包括货币兑换应用程序的全部代码。

清单 9. 完整的货币兑换示例


import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.io.*;
import java.io.*;
import java.lang.*;
import java.util.*;

//A first MIDlet with simple text and a few commands.
public class CurrencyExchange extends MIDlet
               implements CommandListener {

  //The exit commands
  private Command exitCommand; 

  //The display for this MIDlet
  private Display display;

  Form displayForm;

  public CurrencyExchange() {
    display = Display.getDisplay(this);
    exitCommand =
    new Command("Exit", Command.SCREEN, 1);

  }

 // Start the MIDlet by creating the Form and
  // associating the exit command and listener.
  public void startApp() {
   displayForm = new Form("Exchange Rate");
   displayForm.addCommand(exitCommand);
   displayForm.setCommandListener(this);

try
{

   String result = getViaHttpConnection
   ("http://finance.yahoo.com/m5?a=1&s=USD&t=GBP");
   displayForm.append(" " + result);

}
catch (Exception exc)
{
   exc.printStackTrace();
}
display.setCurrent(displayForm);

  }

   // Pause is a no-op because there is no   background
  // activities or record stores to be closed.
  public void pauseApp() { }

  // Destroy must cleanup everything not handled
  // by the garbage collector.
  // In this case there is nothing to cleanup.
  public void destroyApp(boolean unconditional) { }

 // Respond to commands. Here we are only  implementing
 // the exit command. In the exit command,  cleanup and
 // notify that the MIDlet has been destroyed.
  public void commandAction(
  Command c, Displayable s) {
    if (c == exitCommand) {
     destroyApp(false);
     notifyDestroyed();
    }

  }

String parse(String str)
{
   // Get the time of Currency Exchange and Related information
   int timeIndex = str.indexOf("U.S. Markets Closed.");
   String retTime = "";
   if (timeIndex != -1)
   {
     retTime = str.substring(timeIndex-34, timeIndex);
     retTime += " U.S. Markets Closed ";
   }

   String retVal = "";
   int dec = 0;

   int index = str.indexOf("USDGBP");
   if (index != -1)
     str = str.substring(index, str.length());

if ( (( dec = str.indexOf(".")) != -1) && (!(str.endsWith(".")))
&& Character.isDigit(str.charAt(dec+1)) )
{
   String front = "";
   int find = dec-1;
   while (Character.isDigit(str.charAt(find)))
   {
      front += str.charAt(find);
      find--;
   }
   retVal += new StringBuffer(front).reverse().toString();
   retVal += ".";

   String back = "";
   int bind = dec+4;
   while (Character.isDigit(str.charAt(bind)))
   {
     back += str.charAt(bind);
     bind--;
   }
   retVal += new StringBuffer(back).reverse().toString();

}
System.out.println(retVal);

 return "USD/GBP " + retVal + " at " + retTime ;

}

String getViaHttpConnection(String url) throws IOException {
         HttpConnection c = null;
         InputStream is = null;
         StringBuffer str = new StringBuffer();

         try {
             c = (HttpConnection)Connector.open(url);

             // Get the ContentType
             String type = c.getType();

             // Getting the InputStream will open the connection
             // and read the HTTP headers. They are stored until
             // requested.
             is = c.openInputStream();

             // Get the length and process the data
             int len = (int)c.getLength();
             int ch;
             while ((ch = is.read()) != -1) {
                    str.append((char)ch);
                 }

         } finally {
             if (is != null)
                 is.close();
             if (c != null)
                 c.close();
         }

// Before returning, the resultant String should be parsed to get Exchange Rate

String val = parse(str.toString());
System.out.println(val);
return val;
     }

 }

货币值和其它相关信息不久便出现在移动设备的用户界面上。图 2 说明了显示的结果。

图 2. 在手持设备上运行的货币兑换应用程序
在手持设备上运行的货币兑换应用程序

结束语
J2ME 有一种独特的在受约束环境中处理数据输入/输出的精简方式。有了这种功能,支持 Java 的移动配件就不再是简单的通信设备了,而是能够充当微型浏览器的设备,它可以在移动中提供重要的信息。

参考资料

关于作者
Soma Ghosh 是一位计算机科学与工程的毕业生,在过去七年里,她开发了涉及电子交易和联网领域的不计其数的 Java 应用程序。她相信无线交易是业界近年的前途所在,最近她已经投身到了现有台式机组件和模型的无线倡议中。Soma 现在是 Entigo 的一名应用程序开发人员,Entigo 是实时电子商务解决方案和 B2B 销售和服务端电子交易产品的先锋和业界领头羊。您可以通过 sghosh@entigo.com与 Soma 联系。