关于作者

用户名:ilxw
笔名:CnXiaowei
地区: 北京
行业:其他

日历  

快速登录

+ 用户名:
+ 密 码:

在线留言



Google

My Google Search Bar

友情链接

天使ηò哭的BLOG

访问统计:
文章个数:49
评论个数:67
留言条数:4




Powered by BlogDriver 2.1

不是很糊涂

 

文章

Jsr75学习笔记(一)

这段时间因为开发上要用到本地文件,因此开始接触Jsr75的包,Jsr75分两个部分,一部分是File,即本地文件系统,一部分是PIM,说白了就是电话本之类的信息,我先学习的是File部分,通过import javax.microedition.io.file.*里的包,可以实现自由访问本机的文件系统,就跟Windos里的资源管理器一样,在这里我介绍几个我写的方法

一.获取指定路径的目录和文件列表

 /*目录文件列表*/
 public Vector list(String path)
 {
  try
  {
   FileConnection fc=(FileConnection)(Connector.open(path));
   if(fc.exists())
   {
    Vector listVec=new Vector(0,1);
    Enumeration en=fc.list();
    while(en.hasMoreElements())
    {
     listVec.addElement((String)(en.nextElement()));
    }
    return listVec;
   }
   else
   return null;
  }
  catch(Exception e)
  {
   System.out.println("listErr:" e.toString());
   return null;   
  }
 }

方法里的path参数就是要查找的路径,比如file:///c:/pictures/,切记,如果是目录Path,那么后面的/一定不能省,否则会查找不到内容。这个方法返回的是一个包含了指定Path下的所有目录名和文件名的向量。

二.建立或者保存一个文件到指定路径

 /*保存文件*/
 public void saveFile(String path,byte[] fileData)
 {
  try
  {
   FileConnection fc=(FileConnection)(Connector.open(path));
   fc.create();
   fc.setWritable(true);
   OutputStream os=fc.openOutputStream();
   os.write(fileData);
   os.close();
  }
  catch(Exception e)
  {
   System.out.println("saveFileErr:" e.toString());
  }
 } 

fileData是需要保存的文件内容,可以是声音,也可以是图像,或者文字之类

三.删除指定文件

 /*删除文件*/
 public void deleteFile(String path)
 {
  try
  {
   FileConnection fc=(FileConnection)(Connector.open(path));
   if(fc.exists())
   fc.delete();
  }
  catch(Exception e)
  {
   System.out.println("deleteFileErr:" e.toString());
  }
 }

这个方法就不用多解释了

四.读取指定文件

 /*读取文件*/
 public byte[] readFile(String path)
 {
  try
  {
   FileConnection fc=(FileConnection)(Connector.open(path));
   if(fc.exists())
   {
    InputStream is=fc.openInputStream();
    byte[] temp=new byte[is.available()];
    is.read(temp);
    is.close();
    return temp;
   }
   else
   return null;
  }
  catch(Exception e)
  {
   System.out.println("readFileErr:" path e.toString());
   return null;
  }
 }

此方法也不用多解释了。

以上是关于文件操作的最基本的一些功能,也是第一期的学习笔记研究到的内容,更多的内容,过几天继续研究继续写吧,呵呵。

- 作者: CnXiaowei 2007年08月23日, 星期四 23:54  回复(1) |  引用(1) 加入博采

J2ME中的基础碰撞检测算法浅析

在游戏中,经常需要进行碰撞检测的实现,例如判断前面是否有障碍以及判断子弹是否击中飞机,都是检测两个物体是否发生碰撞,然后根据检测的结果做出不同的处理。

进行碰撞检测的物体可能有些的形状和复杂,这些需要进行组合碰撞检测,就是将复杂的物体处理成一个一个的基本形状的组合,然后分别进行不同的检测。

下面简单介绍一下两种最基本的形状进行碰撞的时候进行的处理。

1、矩形和矩形进行碰撞

一般规则的物体碰撞都可以处理成矩形碰撞,实现的原理就是检测两个矩形是否重叠。我们假设矩形1的参数是:左上角的坐标是(x1,y1),宽度是w1,高度是h1;矩形2的参数是:左上角的坐标是(x2,y2),宽度是w2,高度是h2。

在检测时,数学上可以处理成比较中心点的坐标在x和y方向上的距离和宽度的关系。即两个矩形中心点在x方向的距离的绝对值小于等于矩形宽度和的二分之一,同时y方向的距离的绝对值小于等于矩形高度和的二分之一。下面是数学表达式:

x方向:| (x1 w1 / 2) ? (x2 w2/2) | < |(w1 w2) / 2|

y方向:| (y1 h1 / 2) ? (y2 h2/2) | < |(h1 h2) / 2|

在程序中,只需要将上面的条件转换成代码就可以实现了。

但是矩形碰撞只是一种比较粗糙的碰撞检测方法,因为很多实际的物体可能不是一个规则的矩形。

下面介绍一下圆形碰撞。

2、圆形和圆形的碰撞

圆形和圆形的碰撞应该说是一种最简单的碰撞,因为在数学上对于两个圆形是否发生重叠,有计算两个圆心之间的距离的公式。那么条件就变为:计算两个圆心之间的距离是否小于两个圆的半径和。

假设圆形1的左上角坐标是(x1,y1),半径是r1,圆形2的左上角的坐标是(x2,y2),半径是r2。

因为MIDP1.0中没有浮点数,而且浮点数的运算比较慢,所以我们将条件做一个简单的变换:对于条件的两边都进行平方,这样就去掉了开方的运算步骤。

下面是数学表达式:

(x1 ? x2)2 (y1 ? y2)2 < (r1 r2)2

在程序中,只需要将上面的条件转换成代码就可以了。

上面介绍的只是最基本的碰撞检测算法的实现,而实际的编程过程中遇到的碰撞检测问题要比这些复杂很多,还需要其他形式的检测,还需要进行更加深入的学习。

- 作者: CnXiaowei 2007年08月23日, 星期四 23:54  回复(1) |  引用(1) 加入博采

RPG游戏引擎的设计原理
角色扮演游戏(RPG)是深受广大游戏迷们喜爱的一种游戏, 它以独特的互动性和故事性吸引了无数的玩家。它向人们提供了超出现实生活的广阔的虚拟世界,使人们能够尝试扮演不同的角色,去经历和体验各种不同的人生旅程或奇幻经历。这些体验都是在现实生活中无法实现的。在玩过许多游戏后,许多玩家都不再仅仅满足于一个游戏玩家的身份,而会思考游戏是如何制作的,并且打算制作一个自己的游戏,网上的各种游戏制作小组更是如雨后春笋般涌现。下面我就给大家介绍一下角色扮演游戏引擎的原理与制作,希望能对游戏制作爱好者有所帮助。

  一 游戏引擎的原理

  说到引擎,游戏迷们都很熟悉。游戏引擎是一个为运行某一类游戏的机器设计的能够被机器识别的代码(指令)集合。它象一个发动机,控制着游戏的运行。一个游戏作品可以分为游戏引擎和游戏资源两大部分。游戏资源包括图象,声音,动画等部分,列一个公式就是:游戏=引擎(程序代码) 资源(图象,声音,动画等)。游戏引擎则是按游戏设计的要求顺序的调用这些资源。

  二 角色扮演游戏的制作

  一个完整的角色扮演游戏的制作从大的分工来说可以分为:策划,程序设计,美工,音乐制作以及项目管理,后期的测试等。

  策划主要任务是设计游戏的剧情,类型以及模式等,并分析游戏的复杂性有多大,内容有多少,策划的进度要多快等因素。

  程序设计的任务是用某种编程语言来完成游戏的设计,并与策划配合,达到预期的目的。

  美工主要是根据游戏的时代背景与主题设计游戏的场景及各种角色的图象。

  音乐制作是根据游戏的剧情和背景制作游戏的音乐与音效。

  项目管理主要是控制游戏制作的进程,充分利用现有的资源(人员,资金,设备等),以达到用尽量少的资金实现最大的收益。

  后期的测试也是非常重要的一个环节,对于一个几十人花费几个月甚至是几年时间制作的游戏,测试往往能找到许多问题,只有改进程序才能确保游戏的安全发行。

  由于文章主要是讲解游戏程序的制作的,所以策划,美工,音乐制作等方面请读者参考其它文章,下面我就讲讲游戏程序的设计。

  (一) 开发工具与主要技术

  1.件开发工具

  游戏程序开发工具有很多,在不同游戏平台上有不同的开发工具。在个人计算机上,可以用目前流性的软件开发工具,比如:C,C ,VC ,Delphi,C Builder等。由于Windows操作系统的普及和其强大的多媒体功能,越来越多的游戏支持Windows操作系统。由于VC是微软的产品,用它来编写Windows程序有强大的程序接口和丰富的开发资源的支持,加之VC严谨的内存管理,在堆栈上良好的分配处理,生成代码的体积小,稳定性高的优点,所以VC 就成为目前游戏的主流开发工具。

  2.DirectX组件的知识

  谈到Windows系统下的游戏开发,我们就要说一下微软的DirectX SDK。

  Windows系统有一个主要优点是应用程序和设备之间的独立性。然而应用程序的设备无关性是通过牺牲部分速度和效率的到的,Windows在硬件和软件间添加了中间抽象层,通过这些中间层我们的应用程序才能在不同的硬件上游刃有余。但是,我们因此而不能完全利用硬件的特征来获取最大限度的运算和显示速度。这一点在编写Windows游戏时是致命的,DirectX便是为解决这个问题而设计的。DirectX由快速的底层库组成并且没有给游戏设计添加过多的约束。微软的DirectX软件开发工具包(SDK)提供了一套优秀的应用程序编程接口(APIs),这个编程接口可以提供给你开发高质量、实时的应用程序所需要的各种资源。

  DirectX的6个组件分别是:

  DirectDraw: 使用页面切换的方法实现动画,它不仅可以访问系统内存,还可以访问显示内存。

  Direct3D: 提供了3D硬件接口。

  DirectSound: 立体声和3D声音效果,同时管理声卡的内存。

  DirectPlay: 支持开发多人网络游戏,并能处理游戏中网络之间的通信问题。

  DirectInput: 为大量的设备提供输入支持。

  DirectSetup: 自动安装DirectX驱动程序。

  随着DirectX版本的提高,还增加了音乐播放的DirectMusic。

  3.AlphaBlend 技术

  现在许多游戏为了达到光影或图象的透明效果都会采用AlphaBlend 技术。所谓AlphaBlend技术,其实就是按照"Alpha"混合向量的值来混合源像素和目标像素,一般用来处理半透明效果。在计算机中的图象可以用R(红色),G(绿色),B(蓝色)三原色来表示。假设一幅图象是A,另一幅透明的图象是B,那么透过B去看A,看上去的图象C就是B和A的混合图象,设B图象的透明度为alpha(取值为0-1,0为完全透明,1为完全不透明),Alpha混合公式如下:

  R(C)=alpha*R(B) (1-alpha)*R(A)

  G(C)=alpha*G(B) (1-alpha)*G(A)

  B(C)=alpha*B(B) (1-alpha)*B(A)

  R(x)、G(x)、B(x)分别指颜色x的RGB分量原色值。从上面的公式可以知道,Alpha其实是一个决定混合透明度的数值。应用Alpha混合技术,可以实现游戏中的许多效,比如火光、烟雾、阴影、动态光源等半透明效果。

  4.A*算法

  在许多游戏中要用鼠标控制人物运动,而且让人物从目前的位置走到目标位置应该走最短的路径。这就要用到最短路径搜索算法即A*算法了。

  A*算法实际是一种启发式搜索,所谓启发式搜索,就是利用一个估价函数评估每次的的决策的价值,决定先尝试哪一种方案。如果一个估价函数可以找出最短的路径,我们称之为可采纳性。A*算法是一个可采纳的最好优先算法。A*算法的估价函数可表示为:

  f(n) = g(n) h(n)

  这里,f(n)是节点n的估价函数,g(n)是起点到终点的最短路径值,h(n)是n到目标的最断路经的启发值。由于A*算法比较复杂,限于篇幅,在此简单介绍一下,具体理论朋友们可以看人工智能方面的书籍了解详细的情况。

  其它技术还有粒子系统,音频与视频的调用,图象文件的格式与信息存储等,大家可以在学好DirectX的基础上逐渐学习更多的技术。

  (二)游戏的具体制作

  1.地图编辑器的制作

  RPG游戏往往要有大量的场景,场景中根据需要可以有草地,湖泊,树木,房屋,家具等道俱,由于一个游戏需要很多场景且地图越来越大,为了节省空间,提高图象文件的可重用性,RPG游戏的画面采用很多重复的单元(可以叫做“图块”)所构成的,这就要用到地图编辑器了。我们在制作游戏引擎前,要完成地图编辑器的制作。在 RPG游戏里,场景的构成,是图块排列顺序的记录。首先制定一个场景构成文件的格式,在这个文件里记录构成场景所需要的图块的排列顺序,因为我们已经为每个图块建立了索引,所以只需要记录这些索引就可以了。一个场景的构成,是分成几层来完成的:地面,建筑和植物,家具摆设,和在场景中活动的人物或者物体(比如飘扬的旗帜),按照一定的顺序把它们依次显示到屏幕上,就形成了一个丰富多采的场景。我们可以用数组来表示地图场景的生成过程。

  MapData[X][Y]; //地图数据,X表示地图宽度,Y表示地图高度

  Picture[num]; //道具的图片,num表示道具的总数

  void MakeBackGround() //生成场景函数

  {

  int n;

  for( int i=0; i<Y; i ) //共Y行

  for( int j=0; j<X; j ) //共X列

  {

  n=MapData[ i ][ j ]; //取得该位置的道具编号

  Draw( j*32, i*32, Picture[n]); //在此位置(j*32,i*32)画道具

  }

  }

  2.游戏的模块的划分

  游戏按功能分为:消息处理系统、场景显示及行走系统、打斗系统三大主要部分。其中又以消息处理系统为核心模块,其余部分紧紧围绕它运行。

  一:消息处理系统

  消息处理系统是游戏的核心部分。游戏用到的消息处理系统先等待消息,然后根据收到的消息转到相应的函数进行处理。比如:主角碰到敌人后,我们就让程序产生‘打斗消息’,消息处理系统收到这个消息后就会马上转到打斗模块中去。消息处理的大体框架如下:

  //定义程序中要用到的变量

  DWORD Message; //消息变量

  WinMain() //进入程序

  { 初始化主窗口;

  初始化DirectDraw环境,并调入程序需要的图形、地图数据;

  while( 1 ) //消息循环

  { switch( Message )

  { case 行走消息: 行走模块();

  case 打斗消息: 打斗模块();

  case 事件消息: 事件模块();

  }

  }

  }

  二:场景显示及行走系统

  作为RPG游戏,其所有事件的发生几乎都是和场景有关,例如:不同的地方会碰到不同的敌人、与不同的人对话得知不同的事情等。鉴于这部分的重要性,我们可再将它划分为:背景显示、行走 和 事件发生 三个子模块,分别处理各自的功能。下面进行具体分析。

  (一)背景显示

  程序运行后,先读取前面地图编辑器制作的场景所需要的图块的排列顺序,按照排列顺序将图象拼成一个完整的场景,一般做法是:在内存中开辟一到两个屏幕缓存区,事先把即将显示的图象数据准备在缓存区内,然后一次性搬家:把它们传送到真正的屏幕缓冲区内。

  游戏用到的图片则事先制作好并存于另外的图形文件中。地图编辑器制作的场景文件仅仅是对应的数据,而不是真正的图片。在游戏中生成场景就是地图编辑的逆过程,一个是根据场景生成数据,而另一个是根据数据生成场景。

  (二)行走

  要让主角在场景中行走,至少要有上、下、左、右四个行走方向,每个方向4幅图(站立、迈左腿、迈右腿、迈左腿),如图:游戏中一定要将图片的背景设为透明,这样在画人物的时候就不会覆盖上背景色了(这一技术DirectDraw中只要用SetColorKey()函数将原图片背景色过滤掉就行了)。我们让主角位置不动,而使场景移动,即采用滚屏技术来实现角色在场景上移动。这样角色一直保持在屏幕的正中间,需要做的工作只是根据行走方向和步伐不停变换图片而已。行走时的障碍物判断也是每一个场景中必定要有的,有一些道具如树木、房屋等是不可跨越的。对此我主要用一个二维数组来对应一个场景,每一个数组值代表场景的一小格(见图3)。有障碍的地方,该数组的对应值为1,可通过的地方的值为0。

  (三)事件发生

  事件发生原理就是把相应事件的序号存储在地图的某些格子中,当主角一踏入这个格子就会触发对应事件。例如:在游戏开始时,主角是在他的家里。他要是想出去的话,就需要执行场景切换这个处理函数。我们假定该事件的编号为001,那么在地图上把家门外路口处的格子值设为001。这样主角走到路口时,编号为001的场景切换函数就会被触发,于是主角便到了下一个场景中。程序具体如下:

  void MessageLoop( int Msg ) //消息循环

  {switch( Msg )

  {char AddressName[16]; //数组AddressName[16]用来存储主角所在地点的名称

  case ADDRESS == 001: // 由ADDRESS的值决定场景值(出门)

  ScreenX=12; ScreenY=0; //初始化游戏背景位置

  Hero.x=360; Hero.y=80;//主角坐标

  Move();//主角移动函数

  //以下程序用来显示主角所在地点

  sprintf(AddressName,"下一幅游戏场景的名称");

  PrintText(lpDDSPrimary, 280, 330,Address>

  break;}

  }

  三:打斗系统

  绝大多数的RPG都是有战斗存在的,因此,打斗系统就成为RPG系统中很重要的一环。有不少RPG游戏采用回合制打斗方式,因为实现起来较为简单。和打斗紧密相关的是升级,通常在一场战斗结束后,主角的经验值都会增加。而当经验值到达一定程度时,角色就升级了。

  上面我简要的介绍了角色扮演游戏的制作,由于写这篇文章的目的是让读者对角色扮演游戏的制作有一个基本的了解,所以读者朋友们可以研究相关资料。

- 作者: CnXiaowei 2007年08月23日, 星期四 23:53  回复(1) |  引用(1) 加入博采

IIS Resin集成多个站点和数据库连接池
做web开发一直都是使用Resin作为容器,配置起来非常的方便,启动和执行的速度都很快。Resin的官方站点说Resin自带的web服务器速度也非常之快,因此可以不用和apache or iis进行集成。当然,如果由于项目需要集成在一起也很正常。需要注意的是isapi_srun.dll的版本需要注意,有个resin版本带的这个文件如果和iis集成在一起的话,速度非常之慢。我用的3.0.23的,速度还可以接受。关于Resin如何和IIS集成,这里不谈了,网上可以找到很多。

如果你想集成多个数据库连接池,那么就需要多配置几个<database></database>选项,这样你可以在Java程序中使用了,比如hibernate。例如
    <database>
      <jndi-name>jdbc/aaa</jndi-name>
      <driver type="net.sourceforge.jtds.jdbc.Driver">
        <url>jdbc:jtds:sqlserver://localhost/bupt</url>
        <user>ddd</user>BR>        <password>ddd</password>
      </driver>
      <prepared-statement-cache-size>32</prepared-statement-cache-size>
      <max-connections>20</max-connections>
      <max-idle-time>120s</max-idle-time>
    </database>
   
     <database>
      <jndi-name>jdbc/ddd</jndi-name>
      <driver type="net.sourceforge.jtds.jdbc.Driver">
        <url>jdbc:jtds:sqlserver://localhost/mobi</url>
        <user>ddd</user>
        <password>ddd</password>
      </driver>
      <prepared-statement-cache-size>32</prepared-statement-cache-size>
      <max-connections>20</max-connections>
      <max-idle-time>120s</max-idle-time>
    </database>

如果需要配置多个虚拟主机,那么需要在IIS中首先配置多个站点,每个站点靠主机头来区分。并且每个站点都要建立一个虚拟目录,把IIS目录下的scripts加入进来,当然这里你已经集成好了resin。随后需要更改resin的配置文件resin.conf。添加几个host项目既可。比如
    <host id="www.abc.com" root-directory=".">
      <!--
         - configures an explicit root web-app matching the
         - webapp's ROOT
        -->
      <web-app id="/" document-directory="E:eclipseworkspacebuptweb"/>
    </host>
   
        <host id="www.abcd.com" root-directory=".">
      <!--
         - configures an explicit root web-app matching the
         - webapp's ROOT
        -->
      <web-app id="/" document-directory="E:eclipseworkspacemobiweb"/>
    </host>
这里的host的id需要和iis中的站点的主机头对应

- 作者: CnXiaowei 2007年08月21日, 星期二 17:35  回复(1) |  引用(1) 加入博采

J2ME应用程序架构模型

做了快一年的手机应用程序,抽空写点对应用程序架构的认识吧。本文以J2ME手机应用程序为实例。介绍一下在企业应用的开发中,都会涉及到哪些方面的程序架构特征。


J2ME技术从诞生到现在还处于不断的发展壮大中,虽然各种手机对JSR标准支持的差异,各厂商的虚拟机实现的差异,或多或少的阻碍了J2ME最大的优势————跨平台性的体现,但是也不可否认,J2ME技术仍然是当前作为移动开发领域最主要的技术之一。因为它开发简便,可移植性强,使从事JAVA的开发者可以很快的投入J2ME开发,相比symbian平台的C++开发可谓是容易许多。

作为企业客户的应用,现在以C/S模式为主,服务器端保存用户数据,以及响应在线用户的复杂业务逻辑。客户端作为数据的接收者,处理简单的业务逻辑,保存少量的数据,例如聊天纪录,本地账号和密码等。当然客户端也可能是PC。


(图1)

比如一款手机的即时通讯软件IM(Instant Message),服务器端需要支持多客户端同时在线,保存客户信息,处理每个客户端发过来的请求,将A客户的信息转发给B客户,将C客户的离线信息转发给C客户的所有好友,等等。也就是说一个客户端于另一个客户端所有的沟通都要通过服务器去中转。下面来介绍一下客户端的一个简单的架构模型。

我们可以用MVC的设计理念来设计这个框架。大致可分为以下几个层次。

Handler:处理网络请求,网络消息分发,这个层面是最关键的,它会涉及到整体结构的每一层。

Database:用来存储少量数据,在J2ME中,RMS(Record Management System)是这个层次主要承担者。每个手机可能对RMS的大小支持不同,使这宝贵的存储空间更加珍贵。Handler会控制对Database的存储和提取,用来View层显示。

Model: 这个层次主要是根据程序需要创建的一些逻辑控制类。把数据封装,创建公共接口等。比如一条消息,可能会创建一种Message类型来存储在数据库中。

View: 这层用来显示用户界面,并且响应和处理键盘的指令。将Handler层指派的一些信息显示出来,并且将需求信息送给Handler去处理。所以这层直接于Handler沟通,不会直接涉及到Database或网络信息。

 

(图2) 

当然在实际的应用开发中会更复杂一些。比如会有一个session的东西来控制消息的轮询和网络连接。下面我通过一个伪代码来进一步说明这个模型。


/**View:Display messsage, Receive input*/

package view;

import.....

public class view extends Canvas
{
  public String getUserName();
  public String getUserPassword();
  public void displayMessage(Graphics g)
 {   
    g.drawString(Handler.getInstance().getMessageContent(),x,y,0);
 }
  public void commandAction(Command c,Displayable d)
 {
 Handler.getInstance().setUserInfo(getUserName(),getUserPassword());
 }
}
/**************************************/

/*Handler:Control network message*/
package handler;

import......

public class Handler
{
  public sendUserInfo(String name,String password);
  public void getResponse()
 {
   if(ok)
  {
      notfiyDisplay();
  }

 }
  public void notfiyDisplay()
 {
    Message mess = new Message( mess.toString() ) ;
    Database.putMessage( mess )
   
    View.getInstance().repaint();
   
 }
  public String getMessageContent()
 {
    Message m = Database.getInstance.getMessage();
    return m.getContent();
 }
}
/************************************/
/*Message: a model indicate message object*/
package model;

import......
public class Message
{
  public Message(String str)
 {
 }
  public String getContent()
 {
 }
}
/**********************************/

/*Database: store bytes*/
package database;
import......
public DatabaseBR>{
   public void putMessage(Message mess)
 {
 }

   public Message getMessage()
 {
 }
}
/***********************************/

复杂的应用还要处理多媒体内容,各种类型的连接方式,而且对于线程管理,网络连接都要求更高。值得注意的是,一般应用的代码是可以复用的。这样只要改一些View层的显示,就可以适应不同的应用类型了。欢迎加我MSN讨论应用开发,k7sem_88@hotmail.com 

- 作者: CnXiaowei 2007年08月21日, 星期二 17:35  回复(1) |  引用(1) 加入博采

我的j2me创意--Wap浏览器的源代码
这几天不断的有网友,朋友,公司来询问我关于Wap浏览器,即时信息内容设计,以及低级UI组件设计的问题。为了解答各位网友跟朋友的需求,再次贡献自己的源代码,这些代码直接在eclipse+eclipseMe上可直接运行
里面实现的功能友通过kxml解释wml并生成UI组件显示出来。采用多线程下载数据,
本想采用proxy的方式去读取图片---这部分没做
采用mvc结构分离业务逻辑与UI。
采用command的方式进行操作。
实现大部分实用的UI使用,也可以自己定义UI。
。。。。。。。。。。。。还有很多没实现,就等大家去做了。
再次声名,次相关的源代码可以用于商业用途,但用的时候麻烦通知下我。让我也了解我对j2me做成的贡献有多大。也算是对本人我的尊重。
代码写的比较差。还望大家完善

设计一个J2me Wap浏览器幻想
关键字: J2ME       
       昨天完成了一个小程序,所以今天有比较空闲了,前阵子自己设计了j2me的UI,所以对在一手机画布上绘制UI也算是比较有心得的。所以今天突发奇想,不如自己定制一个wap浏览器来玩下。
       自己以前也做过wap网站,所以也知道它的大概个标签,想来通过xml包解释这些标签不难,现在把我的个人想法发到这里来,希望各位爱好者提供建议。
       1:设计一个可组装的UI以提供对xml解释后的支持。(基本上是采用以前的UI设计方式)。
       2: 采用网站流行的xml解释包,对wml进行解释,主要构思是采用一个2维表进行数据结构的保存。
       3:session机制,这方面是个难度,记得以前设计wap的时,在测试的时候发现有些手机并不支持session。个人想法是在手机上保存一个sessionId,然后发送这个Id,并时刻保持这个Id与服务器的一致。
       4:解释机制,是否是一边解释一边显示,还是全部下载完,解释完在显示,在技术上的难度来讲,第一个无疑是比较困难的,但是第二个在用户体验方面存在比较大的缺陷。
       5:缓存机制,主要难点是记录用户的操作,以及判断服务器内容是否改变,从而决定是否重新下载,还是直接从缓存中获取。

     以上几点我只对第一点有把握,第2个估计看下文档就ok了,其他方面我还没有比较多的认识

创意的起源
1。源自去年6月份实现了一个足球的客户端,对即时网络开发有一定的了解,
2。来自足球项目自己抽象出一套可修改,拔插的UI实现。
3。简单其他开源的灵感。

实现过程。
Wap Explorer 图片预览
  前几天说要设计一个Wap浏览器的,经过上个礼拜天的设计,基本已经把UI的框架搭建出来了。不过对于wap的Wml解释器还没写,一来这方面比较难写,没有实践过,二来这几天公司有事情忙。所以就停了下来,不过趁着今天下午有空,我发些设计好的截图给大家看。
         在UI里我实现了对字体的控制已经对皮肤(所谓的皮肤只是换换颜色而已)的控制
{1E0DF53D-767F-4C53-93F1-74D307C3E874}0.jpg {58DB28A3-11CB-4F4A-B864-B9282BF44468}0.jpg {D1C1BED4-7807-4D6A-A6E5-832166EC8ADA}0.jpg

- 作者: CnXiaowei 2007年08月21日, 星期二 17:35  回复(1) |  引用(1) 加入博采

中断JavaME手机上的屏幕保护程序

如果你的应用程序不要求用户连续按键,那么在SonyEricsson的手机上运行的程序,可能一段时间之后就会出现屏幕保护界面,这是手机自带的功能。但是我们可以通过Nokia UI API来设置背景灯来阻止屏幕保护界面。但是持续让背景灯打开会消耗更多的电量。

请注意:对于UIQ3的手机,例如W950 M600等,可以从控制面板来禁止屏幕保护程序,因此Nokia UI API不再适合。

还需要注意的是由于我们并非在程序中禁止屏幕保护程序,而是中断其运行。因此要在循环中不停的调用setLights()方法。

DeviceControl.setLights(0, 100);  

下面是一个案例

import com.nokia.mid.ui.DeviceControl;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.*;

public class BacklightWorkaround extends MIDlet {
 
  private SimpleCanvas canvas;
 
  /**
   * Keeps the backlight on by repeatedly setting
   */
  class LightThread extends Thread {
    public void run() {
      while(true){
       
   &bsp;    DeviceControl.setLights(0, 100);       
        try {
          Thread.sleep(5000);
        } catch (InterruptedException ex) {
          ex.printStackTrace();
        }
      }
    }
  }
 
  private class SimpleCanvas extends Canvas implements CommandListener{
    private Command exitCmd;
    private MIDlet midlet;
   
    public SimpleCanvas(MIDlet midlet) {
      this.midlet = midlet;
      exitCmd = new Command("Exit",Command.EXIT, 1);
      addCommand(exitCmd);
      setCommandListener(this);
    }
    public void paint(Graphics g) {     
      g.drawString("Let there be light.", 0, 0, Graphics.LEFT|Graphics.TOP);
    }

    public void commandAction(Command command, Displayable displayable) {
      if(command == exitCmd){
        midlet.notifyDestroyed();
      }
    }
  }
 
  public void startApp() {
    if(canvas == null){
      canvas = new SimpleCanvas(this);
      new LightThread().start();
    }
   
    Display.getDisplay(this).setCurrent(canvas);
  }
 
  public void pauseApp() { }
 
  public void destroyApp(boolean unconditional) { }
}


- 作者: CnXiaowei 2007年08月21日, 星期二 17:34  回复(1) |  引用(1) 加入博采

游戏开发中的经典算法集锦之一

算法是程序设计的精髓,程序设计的实质就是构造解决问题的算法,将其解释为计算机语言。

算法是在有限步骤内求解某一问题所使用的一组定义明确的规则。通俗点说,就是计算机解题的过程。在这个过程中,无论是形成解题思路还是编写程序,都是在实施某种算法。前者是推理实现的算法,后者是操作实现的算法。

一个算法应该具有以下五个重要的特征:

  1. 有穷性: 一个算法必须保证执行有限步之后结束;
  2. 确切性: 算法的每一步骤必须有确切的定义;
  3. 输入:一个算法有0个或多个输入,以刻画运算对象的初始情况;
  4. 输出:一个算法有一个或多个输出,以反映对输入数据加工后的结果。没有输出的算法是毫无意义的;
  5. 可行性: 算法原则上能够精确地运行,而且人们用笔和纸做有限次运算后即可完成。

动态规划-航线设置


问题描述:美丽的莱茵河畔,每边都分布着N个城市,两边的城市都是唯一对应的友好城市,现需要在友好城市开通航线以加强往来.但因为莱茵河常年大雾,如果开设的航线发生交叉现象就有可能出现碰船的现象.现在要求近可能多地开通航线并且使航线不能相交!

   假如你是一个才华横溢的设计师,该如何设置友好城市间的航线使的航线数又最大又不相交呢?

   分析:此问题可以演化成求最大不下降序列来完成.源程序如下:

program dongtai;  {动态规划之友好城市航线设置问题}
var
 d:array[1..1000,1..4] of integer;
 i,j,k,n,L,p:integer;

 procedure print(L:integer);  {打印结果}
 begin
 writeLn('最多可设置的航线数是 : ',k);
   repeat
     writeLn(d[L,1]:4,d[L,2]:4); {输出可以设置航线的友好城市代码}
     L:=d[L,4]
   untiL L=0
 end;

begin
 writeLn('输入友好城市对数: ');
 readLn(n);
 writeLn('输入友好城市对(友好城市放在同一行:'); {输入}
 for i:=1 to n do
    readLn(d[i,1],d[i,2]);  {D[I,1]表示起点,D[I,2]表示终点}
 for i:=1 to n do
    begin
       d[i,3]:=1;  {D[I,3]表示可以设置的航线条数}
       d[i,4]:=0   {D[I,4]表示后继,即下一条航线从哪里开始设置,为0表示不能设置下一条航线}
    end;
for i:=n-1 downto 1 do  {从倒数第二个城市开始规划}
   begin
     L:=0;  p:=0;  {L表示本城市后面可以设置的航线数,P表示下条航线从哪个城市开始}
     for j:=i 1 to n do  {找出本城市后面可以设置的最大航线数和小条航线到底从哪个城市开始设置}
       if (d[i,2] L) then 
                                                {如果本城市I的终点小于后面城市的终点(即不相交)}                                      {并且此城市后面可以设置的航线数大于L}
          begin
            L:=d[j,3];   {那么L等于城市J的可以设置航线数}
            p:=j         {P等于可以设置下条航线的城市代码}
          end;
     if L>0 then   {如果本城市后面总共可以设置的航线数>0则}
         begin
           d[i,3]:=L 1;  {本城市可以设置的航线数在下个城市可以设置航线数的基础上加1}
           d[i,4]:=p     {D[I,4]等于本城市后续城市的代码}
         end
   end;
   k:=d[1,3];  {K为可以设置最大航线数,假设初值为第一个城市可以设置的航线数}
   L:=1;       {L为城市代码,初值为第一个城市}
   for i:=2 to n do  {找出可以设置航线的最大值,赋值给K,同时L记下哪个可以设置最大航线数的城市代码}
     if d[i,3]>k then
        begin
          k:=d[i,3];
       &nbs;  L:=i
        end;
   for i:=1 to n do  {打印结果,因为有可能有多种方案,所以只要哪个城市可以设置的航线数等于最大值K就打印结果}
     if d[i,3]=k then print(i)

end.

归并排序算法

合并排序(MERGE SORT)是又一类不同的排序方法,合并的含义就是将两个或两个以上的有序数据序列合并成一个新的有序数据序列,因此它又叫归并算法。它的基本思想就是假设数组A有N个元素,那么可以看成数组A是又N个有序的子序列组成,每个子序列的长度为1,然后再两两合并,得到了一个  N/2   个长度为2或1的有序子序列,再两两合并,如此重复,值得得到一个长度为N的有序数据序列为止,这种排序方法称为2—路合并排序。

例如数组A有7个数据,分别是: 49   38   65  97   76   13   27,那么采用归并排序算法的操作过程如图7所示:

初始值                 [49]   [38]   [65]   [97]   [76]   [13]   [27]

 看成由长度为1的7个子序列组成

      第一次合并之后         [38     49]    [65    97]   [13     76]   [27]

看成由长度为1或2的4个子序列组成

第二次合并之后         [38     49      65    97]   [13     27     76]

看成由长度为4或3的2个子序列组成

      第三次合并之后         [13     27      38    49     65      76    97]

                          图6    归并排序算法过程图

合并算法的核心操作就是将一维数组中前后相邻的两个两个有序序列合并成一个有序序列。合并算法也可以采用递归算法来实现,形式上较为简单,但实用性很差。

合并算法的合并次数是一个非常重要的量,根据计算当数组中有3到4个元素时,合并次数

是2次,当有5到8个元素时,合并次数是3次,当有9到16个元素时,合并次数是4次,按照这一


 

X
 
规律,当有N个子序列时可以推断出合并的次数是X(2  >=N,符合此条件的最小那个X)。
源程序:

program hebing;
const
  n=10;
var
  a:array[1..n] of integer;
  i:integer;

procedure init;
  var
    i:integer;
  begin
    for i:=1 to n do read(a[i]);
    readln;
  end;

procedure merge(low,mid,high:integer);
  var
    h,i,j,k:integer;
    b:array[1..n] of integer;

  begin
    h:=low; i:=low; j:=mid 1;
    while (h<=mid) and (j<=high) do
      begin
        if (a[h]<=a[j]) then
          begin
            b[i]:=a[h]; h:=h 1;
          end
        else
          begin
            b[i]:=a[j]; j:=j 1;
          end;
        i:=i 1;
      end;
    if h>mid then
      for k:=j to high do
        begin
          b[i]:=a[k]; i:=i 1;
        end
    else
      for k:=h to mid do
        begin
          b[i]:=a[k]; i:=i 1;
        end;
    for k:=low to high do
      a[k]:=b[k];
  end;

procedure mergesort(low,high:integer);
  var
    mid:integer;
  begin
    if low<high then
       begin
         mid:=(low high) div 2;
         mergesort(low,mid);
         mergesort(mid 1,high);
         merge(low,mid,high);
       end;
  end;

枚举法

有4个学生,上地理课时提出我国四大谈水湖的排列次序如下:

甲:洞庭湖最大,洪泽湖最小,鄱阳湖第三;

乙:洪泽湖最大,洞庭湖最小,鄱阳湖第二,太湖第三;

丙:洪泽湖最小,洞庭湖第三;

丁:鄱阳湖最大,太湖最小,洪泽湖第二,洞庭湖第三;

   对于各湖泊应处的位置,每个人只说对了一个。根据以上描述和条件,编写程序,让计算机判断一下各湖泊应该处于第几位。

 

解题思路:设置洞庭湖、洪泽湖、鄱阳湖、太湖分别用字母A、B、C、D代表,最大到最小依次用数字4——1表示。应用枚举法可以解决此问题,不过稍微复杂罗嗦点。

源程序如下:

program hupo;
var
  a,b,c,d:integer;
begin
  for b:=1 to 4 do
    for a:=1 to 4 do
      if ((b=1) and (a<>2)) or ((a=2) and (b<>1)) then
       if a<>b then
       for c:=1 to 4 do
         if (a<>c) and (b<>c) then
          if (a b c<>7) and (a b<>5) and (a c<>6) and (b c<>3) then
          for d:=1 to 4 do
           if c<>d then
            if  (b a<>5) and (b c<>7) and (b d<>6) then
               if (a c<>4) and (a d<>3) and (c d<>5) then
                 if (c d<>5) and (c b<>7) and (c a<>6) then
                   if (d b<>4) and (d a<>3) and (b a<>5) then
                        writeln('a=',a,' b=',b,' c=',c,' d=',d)

end.

改进程序:

program ygzj;
var
a,b,c,d:integer;
begin
for a:=1 to 4 do
  for b:=1 to 4 do
    for c:=1 to 4 do
     begin
      d:=10-a-b-c;
      if ord(a=1) ord(b=4) ord(c=3)=1 then
        if ord(b=1) ord(a=4) ord(c=2) ord(d=3)=1 then
          if ord(b=4) ord(a=3)=1 then
            if ord(c=1) ord(d=4) ord(b=2) ord(a=3)=1 then
              if a*b*c*d=24 then
              writeln('洞庭湖第':3,a:3,'洪泽湖第':3,b:3,'波阳湖第':3,c:3,'太湖第':3,d:3);
      end;
writeln
end.

数字全排列问题:


任意给出从1到N的N个连续的自然数,求出这N个自然数的各种全排列。如N=3时,共有以下6种排列方式:
123,132,213,231,312,321。
注意:数字不能重复,N由键盘输入(N<=9)。

解题思路:
   应用回溯法,每个数的取法都有N个方向(1——N),当取够N个数时,输出一个排列,然后退后一步,取前一个数的下一个方向(即前一个数 1),并且要保证所有数字不能重复。当前数字的所有方向都取完时,继续退一步,一直重复到第一个数为止。

程序代码:

program quanpailie; {数字全排列问题}
var
  a:array[1..9] of integer;
  k,x,n:integer;

  function panduan(j,h:integer):boolean;  {判断当前数字是否能赋值给当前数组元素}
  var
    m:integer;
  begin
    panduan:=true;
    for m:=1 to h-1 do
      if a[m]=j  then panduan:=false  {如果当前数字与前面的数组元素相同则不能赋值}
  end;

  procedure try(h:integer);
  var
    i,j,m:integer;
  begin
       for j:=1 to n do
         if panduan(j,h) then
              begin
                a[h]:=j;  {能够赋值,且长度k加一}
                k:=k 1;
                if k=n then  {如果长度达到N则表示一种组合已经完成,输出结果}
                  begin
                    for m:=1 to n do
                      write(a[m]);
                      write('':4);
                      x:=x 1;  {每输出一种排列方式加一}
                      if x mod 5=0 then writeln; {每行输出5种排列方案}
                  end
                else
                  try(h 1);  {对下一个数组元素进行赋值}
                k:=k-1    {回溯的时候一定要把长度减一}
              end
   end;

begin
  writeln('输入 N:');
  readln(n);
  k:=0; {k表示长度,长度初始值为0}
  x:=0; {x表示总的排列方式}
  try(1); {对第一个数组元素赋值}
  writeln('共有 ', x ,' 种排列方案')
end.

- 作者: CnXiaowei 2007年08月21日, 星期二 17:34  回复(1) |  引用(1) 加入博采

游戏开发中的经典算法集锦之二

Dijkstra最短路径(一点到各顶点最短路径)

{本程序解决6个顶点之间的最短路径问题,各顶点间关系的数据文件在sj.txt中}
{如果顶点I到顶点J不能直达就设置距离为30000}
program dijkstra;
type
   jihe=set of 0..5;
var
   a:array[0..5,0..5] of integer;
   dist:array[0..5] of integer;
   i,j,k,m,n:integer;
   fv:text;
   s:jihe;
begin
   s:=[0];
   assign(fv,'sj.txt');
   reset(fv);
   for i:=0 to 5 do  {从文件中读数据,其中a[i,j]代表从顶点i到顶点j的直达距离,如果不通用30000代替}
     begin
        for j:=0 to 5 do read(fv,a[i,j]);
        readln(fv)
     end;
   for i:=1 to 5 do  {设置DIST数组的初始值,即为顶点0到各顶点的直达距离(算法的第一步)}
      dist[i]:=a[0,i];
   for i:=1 to 5 do
   begin
        m:=0;
        dist[m]:=30000;    {设置DIST[M]的目的是为下面的一步做准备,即在DIST数组中一个最小的值}

        for j:=1 to 5 do    {算法的第二步,找最小的DIST值}
        if (not (j in s)) and (dist[m]>dist[j]) 
         then m:=j ;    {用M来记录到底是哪个顶点}
        s:=s [m];    {把顶点加入S中}

        for k:=1 to 5 do     {算法的第三步,修改后面的DIST值}
           if (not (k in s)) and  (dist[k]>dist[m] a[m,k])
             then
               dist[k]:=dist[m] a[m,k]
   end;
   writeln('原各顶点间的路径关系是:(30000代表不通)');
   for i:=0 to 5 do
      begin
        for j:=0 to 5 do  write(a[i,j]:6);
        writeln
      end;
   writeln; writeln;

八皇后问题

{问题描述:在国际象棋8X8的棋盘里摆放8个皇后,使每个皇后都能生存而不互相冲突,即同一行、同一列同对角线(包括主对角线和辅对角线)都只能有一个皇后}

program eightqueen;  {本程序可以搜索出所有的解}
var
  a,b:array[1..8] of integer;
  c:array[-7..7] of integer;
  d:array[2..16] of integer;
  i,k:integer;  {K变量用来存放答案的个数}
  fv:text;

  procedure print;
  var
    i:integer;
  begin
    for i:=1 to 8 do
       writeln(fv,'第',i:2, '行放在第', a[i]:2,'列');  {结果输出到文件里}
     &nsp; k:=k 1;  {每输出一个答案计数加1}
       writeln(fv)
  end;

  procedure try(i:integer);
  var
    j:integer;
  begin
    for j:=1 to 8 do
      if (b[j]=0) and (c[i-j]=0) and (d[i j]=0) then
         begin
           a[i]:=j; 
           b[j]:=1; {宣布占领列、主副对角线}
           c[i-j]:=1;
           d[i j]:=1;
           if i<8 then try(i 1) else print;
           b[j]:=0;  {释放占领列、主副对角线}
           c[i-j]:=0;
           d[i j]:=0
         end
  end;

begin
  for i:=1 to 8 do a[i]:=0;
  for i:=-7 to 7 do c[i]:=0;
  for i:=2 to 16 do d[i]:=0;
  k:=0;
  assign(fv,'jieguo.txt');  {指定文件与文件变量相联系}
  rewrite(fv);  {以写的方式打开文件}
  try(1);
  close(fv);  {一定要记得关闭文件,不然数据有可能丢失}
  writeln('共有 ',k:3,' 种摆法')
end.

 快速排序算法

program kuaisu(input,output);
const n=10;
var
   s:array[1..10] of integer;
   k,l,m:integer;

procedure qsort(lx,rx:integer);
var
   I,j,t:integer;
Begin
   I:lx;j:rx;t:s[I];
   Repeat
      While (s[j]>t) and (j>I) do
         Begin
            k:=k 1;
            j:=j-1
         end;
   if I<j then
begin
   s[I]:=s[j];I:=I 1;l:=l 1;
   while (s[I]<t) and (I<j) do
       begin
          k:=k 1;
          I:=I 1
      End;
   If I<j then
begin
         S[j]:=s[I];j:=j-1;l:=l 1;
      End;
End;
Until I=j;
S[I]:=t;I:=I 1;j:=j-1;l:=l 1;
If lx<j then qsort(lx,j);
If I<rx then qsort(I,rx)
End;{过程qsort结束}

Begin
Writeln('input 10 integer num:');
For m:=1 to n do read(s[m]);
K:=0;l:=0;
Qsort(l,n);
Writeln('排序后结果是:');
For m:=1 to n do write(s[m]:4)
 End.
地图四色问题

{问题描述:任何一张地图只要用四种颜色进行填涂,就可以保证相邻省份不同色}

program tt;
const num=20;
var a:array [1..num,1..num] of 0..1;
    s:array [1..num] of 0..4; {用1-4分别代表RBWY四种颜色;0代表末填进任何颜色}
    k1,k2,n:integer;
function pd(i,j:integer):boolean;{判断可行性:第I个省填上第J种颜色}
var k:integer;
begin
     for k:=1 to i-1 do   {一直从第一个省开始进行比较一直到I省减一的那个省,目的是对已经着色的省份来进行比较,因为>I的省还没                           有着色,比较没有意义,着色的顺序是先第一、二、三……I个省}
         if (a[i,k]=1) and (j=s[k]) then {省I和省J相邻且将填进的颜色和已有的颜色相同}
            begin
               pd:=false; {即不能进行着色}
               exit;   {退出当前函数}
            end;
     pd:=true;  {可以进行着色}
end;

procedure print;{打印结果}
var k:integer;
begin
      for k:=1 to n do{将数字转为RBWY串}
          case s[k] of
            1:write('R':4);
            2:write('B':4);
            3:write('W':4);
            4:write('Y':4);
          end;
      writeln;
end;

procedure try(i:integer);
var j:integer;
begin
     for j:=1 to 4 do
         if pd(i,j) then begin
                              s[i]:=j;
                              if i=n then print
                                 else try(i 1);  {对下一个省进行着色}
                              s[i]:=0;  {不能进行着色,将当前状态设置0,即不进行着色}
                          end;
end;

BEGIN
     write('please input city number: '); readln(n);
     writeln('please input the relation of the cities:');
     for k1:=1 to n do
     begin
          for k2:=1 to n do read(a[k1,k2]);  {A[K1,K2]=1表示省K1、K2相邻,为0就不相邻}
          readln;
     end;
     for k1:=1 to n do s[k1]:=0;  {把所有的颜色设置为0,即还没有进行着色}
     try(1);
END.


穿越迷宫

{本程序假设迷宫是一个4 X 4的矩阵,入口在A[1,1],出口在A[4,4]}
{矩阵数据放在文件shuju3.txt 中}
program mikong;
var
  a,b,c:array[1..4,1..4] of integer;  {数组A用来存放迷宫路径,约定元素值为0表示通,1表示不通}
                                      {数组B用来存放方向增量} 
                                      {数组C用来记录结果,当第I步移到某一元素时,该元素就等于I}
  i,j,k,m,n:integer;
  fv:text;
  q:boolean;  {用来标记迷宫是否有出路}

  procedure print;
  var
     m,n:integer;
  begin
    q:=true;  {如果打印步骤,表示肯定有出路}
    writeln;
    writeln;
    writeln('穿越迷宫的步骤是:');
    for m:=1 to 4 do
   nbsp;  begin
              for n:=1 to 4 do
          write(c[m,n]:4);
        writeln;
      end
  end;

  procedure try(x,y,i:integer);
  var
    u,v,k:integer;
  begin
    for k:=1 to 4 do   {可以有4个方向选择}
      begin
        u:=x b[k,1];     {当前坐标加上方向增量}
        v:=y b[k,2];
        if (u>=1) and (u<=4) and (v>=1) and (v<=4) then  {判断是否越界}
          if (a[u,v]=0) and (c[u,v]=0) then    {判断是否为0,为0就表示通,为1就表示不通}
            begin
              if (x=2) and (y=3) then writeln('aaaaaaaaaaaa');
              c[u,v]:=i;  {数组 C打上记号,表示此元素是第I步到达}
              if (u<>4) or (v<>4) then  {判断是否到出口}
                  try(u,v,i 1)  {没有就继续走}
                else   print;
              c[u,v]:=0   {下一路所有方向都不通,清除本次所做的标记}
            end
      end
  end;

 

begin
  assign(fv,'shuju3.txt');
  reset(fv);
  for i:=1 to 4 do
    begin
      for j:=1 to 4 do
        read(fv,a[i,j]);
        readln(fv)
      end;
  b[1,1]:=-1;  b[1,2]:=0;
  b[2,1]:=0;   b[2,2]:=1;
  b[3,1]:=1;   b[3,2]:=0;
  b[4,1]:=0;   b[4,2]:=-1;
  close(fv);
  for i:=1 to 4 do    {首先标记数组C所有元素全部为0}
    for j:=1 to 4 do c[i,j]:=0;
  c[1,1]:=1;
  for i:=1 to 4 do  {显示迷宫具体线路,0表示通,1表示不通}
    begin
      for j:=1 to 4 do
        write(a[i,j]:4);
        writeln
      end;
  q:=false;  {假设迷宫没有出路}
  try(1,1,2);
 if q=false then writeln( '此迷宫没有出路')
end.

- 作者: CnXiaowei 2007年08月21日, 星期二 17:34  回复(1) |  引用(1) 加入博采

五子棋的核心算法
五子棋是一种受大众广泛喜爱的游戏,其规则简单,变化多端,非常富有趣味性和消遣性。这里设计和实现了一个人机对下的五子棋程序,采用了博弈树的方法,应用了剪枝和最大最小树原理进行搜索发现最好的下子位置。介绍五子棋程序的数据结构、评分规则、胜负判断方法和搜索算法过程。

一、相关的数据结构
    关于盘面情况的表示,以链表形式表示当前盘面的情况,目的是可以允许用户进行悔棋、回退等操作。
    CList StepList;
    其中Step结构的表示为:

    struct Step
    {
      int  m; //m,n表示两个坐标值
      int  n;
      char side; //side表示下子方
    };
以数组形式保存当前盘面的情况,
目的是为了在显示当前盘面情况时使用:
  char FiveArea[FIVE_MAX_LINE][FIVE_MAX_LINE];

    其中FIVE_MAX_LINE表示盘面最大的行数。

    同时由于需要在递归搜索的过程中考虑时间和空间有效性,只找出就当前情况来说相对比较好的几个盘面,而不是对所有的可下子的位置都进行搜索,这里用变量CountList来表示当前搜索中可以选择的所有新的盘面情况对象的集合:

  CList CountList;
    其中类CBoardSituiton为:
  class CBoardSituation
  {
  CList  StepList; //每一步的列表
  char FiveArea[FIVE_MAX_LINE][FIVE_MAX_LINE];
  struct Step machineStep;    //机器所下的那一步
  double value;  //该种盘面状态所得到的分数
}

二、评分规则
    对于下子的重要性评分,需要从六个位置来考虑当前棋局的情况,分别为:-,&brvbar;,/,,//,\


    实际上需要考虑在这六个位置上某一方所形成的子的布局的情况,对于在还没有子的地方落子以后的当前局面的评分,主要是为了说明在这个地方下子的重要性程度,设定了一个简单的规则来表示当前棋面对机器方的分数。

    基本的规则如下:

判断是否能成5, 如果是机器方的话给予100000分,如果是人方的话给予-100000 分;
判断是否能成活4或者是双死4或者是死4活3,如果是机器方的话给予10000分,如果是人方的话给予-10000分;
判断是否已成双活3,如果是机器方的话给予5000分,如果是人方的话给予-5000 分;
判断是否成死3活3,如果是机器方的话给予1000分,如果是人方的话给予-1000 分;
判断是否能成死4,如果是机器方的话给予500分,如果是人方的话给予-500分;
判断是否能成单活3,如果是机器方的话给予200分,如果是人方的话给予-200分;
判断是否已成双活2,如果是机器方的话给予100分,如果是人方的话给予-100分;
判断是否能成死3,如果是机器方的话给予50分,如果是人方的话给予-50分;
判断是否能成双活2,如果是机器方的话给予10分,如果是人方的话给予-10分;
判断是否能成活2,如果是机器方的话给予5分,如果是人方的话给予-5分;
判断是否能成死2,如果是机器方的话给予3分,如果是人方的话给予-3分。

    实际上对当前的局面按照上面的规则的顺序进行比较,如果满足某一条规则的话,就给该局面打分并保存,然后退出规则的匹配。注意这里的规则是根据一般的下棋规律的一个总结,在实际运行的时候,用户可以添加规则和对评分机制加以修正。

三、胜负判断
    实际上,是根据当前最后一个落子的情况来判断胜负的。实际上需要从四个位置判断,以该子为出发点的水平,竖直和两条分别为 45度角和135度角的线,目的是看在这四个方向是否最后落子的一方构成连续五个的棋子,如果是的话,就表示该盘棋局已经分出胜负。具体见下面的图示:


四、搜索算法实现描述
    注意下面的核心的算法中变量currentBoardSituation,表示当前机器最新的盘面情况, CountList表示第一层子节点可以选择的较好的盘面的集合。核心的算法如下:
void MainDealFunction()
{
  value=-MAXINT; //对初始根节点的value赋值
CalSeveralGoodPlace(currentBoardSituation,CountList);
//该函数是根据当前的盘面情况来比较得到比较好的可以考虑的几个盘面的情况,可以根据实际的得分情况选取分数比较高的几个盘面,也就是说在第一层节点选择的时候采用贪婪算法,直接找出相对分数比较高的几个形成第一层节点,目的是为了提高搜索速度和防止堆栈溢出。
pos=CountList.GetHeadPosition();
CBoardSituation* pBoard;
for(i=0;ivalue=Search(pBoard,min,value,0);
  Value=Select(value,pBoard->value,max);  
  //取value和pBoard->value中大的赋给根节点
}
for(i=0;ivalue)  
//找出那一个得到最高分的盘面
  {
    currentBoardSituation=pBoard;
    PlayerMode=min; //当前下子方改为人
    Break;
  }
}

    其中对于Search函数的表示如下:实际上核心的算法是一个剪枝过程,其中在这个搜索过程中相关的四个参数为:(1)当前棋局情况;(2)当前的下子方,可以是机器(max)或者是人(min);(3)父节点的值oldValue;(4)当前的搜索深度depth。

double Search(CBoardSituation&
board,int mode,double oldvalue,int depth)
{
  CList  m_DeepList;
  if(deptholdvalue))==    TRUE)
      {
          if(mode==max)
            value=select(value,search(successor
          Board,min,value,depth+1),max);
          else
            value=select(value,search(successor
            Board,max,value,depth+1),min);
      }
      return value;
  }
  else
  {
if ( goal(board)<>0)  
//这里goal(board)<>0表示已经可以分出胜负
  return goal(board);
else
  return evlation(board);
        }
    }

    注意这里的goal(board)函数是用来判断当前盘面是否可以分出胜负,而evlation(board)是对当前的盘面从机器的角度进行打分。

    下面是Select函数的介绍,这个函数的主要目的是根据 PlayerMode情况,即是机器还是用户来返回节点的应有的值。

double Select(double a,double b,int mode)
{
  if(a>b && mode==max)&brvbar;&brvbar; (a< b && mode==min)
return a;
  else
return b;
}

五、小结
    在Windows操作系统下,用VC++实现了这个人机对战的五子棋程序。和国内许多只是采用规则或者只是采用简单递归而没有剪枝的那些程序相比,在智力上和时间有效性上都要好于这些程序。同时所讨论的方法和设计过程为用户设计其他的游戏(如象棋和围棋等)提供了一个参考。

- 作者: CnXiaowei 2007年08月21日, 星期二 17:33  回复(1) |  引用(1) 加入博采

在J2ME编程中使用Nokia的中文模拟器

在J2ME编程中,经常需要使用Nokia系列的模拟器,但是Nokia Developer’s Suit for J2ME中提供的三个模拟器都无法正确的显示中文,在程序开发过程中,大家经常要为这个事情头疼。
下面介绍几个可以在编程中支持中文显示的Nokia模拟器:

1、7210模拟器

       如果大家进行是Nokia S40 1.0版本的开发的话,可以使用7210的模拟器,下载地址为:

       http://www.forum.nokia.com/main/0,6566,034-31,00.html

       安装完成以后,在安装目录下的bin目录下打开模拟器,设置“Tools”菜单下的语言为中文简体,重新启动,就可以显示中文了。

2、S40 2.0 SDK

       如果大家进行的是Nokia S40 2.0系列的开发的话,可以使用S40 2.0 SDK中的6230i或者是6255模拟器,下载地址为:

       http://www.forum.nokia.com/main/0,,034-521,00.html

       然后下载一个世界语包,下载地址为:

       http://www.forum.nokia.com/main/0,6566,034-522,00.html

              这样,模拟器就支持中文了。

              而且模拟器的尺寸比128X128要大。

       3、S60系列

              如果大家进行的是60系列的开发的话,可以使用Series 60 Platform SDK's for Symbian OS, for Java里面的中文模拟器,下载地址为:

              http://www.forum.nokia.com/main/0,6566,034-483,00.html

              记得一定要下2nd Edition, FP 2 ? Chinese、2nd Edition, FP 1 ? Chinese或者是2nd Edition ? Chinese。

              这样就可以在S60开发中,使用中文版本的模拟器了,但是这个模拟器的运行速度比较慢,大家要忍受一下。

- 作者: CnXiaowei 2007年08月21日, 星期二 17:33  回复(0) |  引用(1) 加入博采

J2ME专业手机游戏开发基础(2)

状态机
            状态机是编译原理的内容,看上去挺复杂的,不过说白了就是选择分支结构。但我为什么要提状态机呢?其实它是一个简化问题的好工具。再复杂的问题都可以被分解成若干小问题去解决。虽然一个游戏很复杂,但我们把它分解成若干块,分而治之,就简单多了。分类的依据就是状态。我们可以将一个游戏划分成很多状态。比如主菜单状态,控制主角状态,暂停状态等。在状态中可以再细分子状态。一直分下去,直到问题变简单。下面看看某个游戏的gameLoop片断。

public void gameLoop()

    {       

            switch(gameState){

            case GS_Logo:

                logic_Logo() ;

                break ;

            case GS_MainMenu:

                logic_MainMenu() ;

                break ;

            case GS_PlayerControl:   

                logic_PlayerControl() ;

                break ;

            case GS_PauseMenu:

                logic_PauseMenu() ;

                break ;                                                                                                 

            case GS_GameOver:

                endGame() ;

                break ;

            }       

    }

针对不同的状态进入不同的逻辑处理函数,问题就简单多了。从某个角度来说,游戏就是状态的集合,我们要处理好状态之间的转换,写好每个状态的代码。在写状态机之前,最好先画出状态转换图。这样条理清楚,而且便于观看整个游戏的结构。举个例子,下面是一个ARPG游戏中敌人的AI状态图:(图略)
从状态转换图很容易得到AI代码,而且容易检查错误。常犯的错误是没有处理所有可能的状态。
画出状态转换图并仔细检查是个不错的方法.当然对于简单的AI就不必要麻烦了。中等复杂的AI画一个简单的小图就可以解决问题。

 渲染

            首先解释一下渲染这个词,英文是render,用在这里只是绘制的意思,这么用只是一个习惯。上面讲的run函数中并没有任何渲染的代码。这是由J2ME的结构决定的,所有的绘制代码被放在了paint里面。当然如果你使用一个Muttable Image缓冲屏幕,也可以在gameLoop里面对这个Muttable Image进行绘制,然后在paint里一次性的将这个Image贴到屏幕上。不过由于大多数手机硬件上支持双缓冲-paint的参数来自一个后台缓冲,这么做并没有太大的意义,反而白白浪费一块内存。所以J2ME游戏中的渲染往往放在paint中。

            在paint里面我们进行绘制工作也是分状态进行的,比如logo状态,我们在gameLoop里面根据时间(可以用run里面的那个frameCount代替真实时间)设置下一帧将要显示的logo图片,然后在paint里面将当前的图片画出来。其实也有一种写法是将gameLoop的代码写在paint中,因为paint是通过repaint和serviceRepaints每帧强制调用的,所以这样做完全可行。如果这样paint就是gameLoop,不需要另外一个gameLoop了。

            整个游戏的渲染可以按层次分几块进行。首先是场景的渲染,然后是场景中的所有物体,我称之为Sprite。这些Sprite需要根据位置进行排序后按顺序渲染,这样才能显示出正确的遮挡关系。场景有可能是分层的,比如分两层,第一层是地面,第二层是比较高的物体如大树。这样所有的Sprite就要放在这两层之间。当然不排除有些Sprite能飞到树上面,这需要单独处理。我这里说的Sprite并不是MIDP Game API里面的Sprite类,它只是一个概念而以。其实专业手机游戏开发中很少用到Game API,往往是自己开发一套类似于Game API但功能强很多的引擎。对于初学者,可以利用Game API进行比较方便的开发,但一定不能依赖于Game API,毕竟有能力开发出比Game API更好的引擎才是专业游戏开发者应该具有的素质。而且不依赖于某某API对于移植也很有好处。所有的渲染中,最上层的一般是游戏界面(GUI)的绘制。专业的说法是HUD(Headup Display)。这些GUI包括菜单、状态条、数据信息等等。

            渲染除了绘制的简单意思,往往还有一层图形效果的意思。在PC游戏开发中,常常使用Alpha混合、饱和运算、光影等各种效果。这些效果都是通过像素操作实现的。在手机上由于设备能力的限制,这些操作往往速度非常慢,不过随着手机硬件的发展,终究会被用上。除此之外,粒子系统是一种常常使用的效果,在手机游戏开发中也经常运用。有兴趣的读者可以搜索相关的文章学习。

场景与角色

            场景与角色是构成游戏的两大要素。从程序角度说,他们是两个重要的数据结构。本节将从数据结构、渲染方法、物理作用等方面分别讲解。

 

1 场景管理

            场景是什么呢?场景就是游戏角色所存在的世界。在RPG、SLG、RTS等类型的游戏中,场景的概念比较明显,从视觉上看就是游戏的地图。不过从程序的角度看,场景是一种数据结构,不但包含了地图显示的图形信息也包含了角色在场景中活动所需要的物理信息和事件信息。比如地图上有些地方是不可以通过的,有些地方主角走过去会触发一个事件等等,这些信息往往包含在场景中。广义的场景还包含地图上的NPC信息,而狭义的场景仅包含地图和物理层。这里讨论狭义的场景,即不包含NPC信息的场景。

            首先讨论怎样组织图片显示场景。最常用的方法是拼Tile,俗称贴瓷砖。整个场景用有限的图块拼接而成。Game API中的TiledLayer就是这种方式。使用Tile方式,一般用一个二维数组表示一张地图。实际使用中,往往使用一维数组代替二维数组,这里为了讲解方便使用二维数组描述。二维数组很好的对应了二维坐标。二维数组的每一项对应地图上某格使用那个瓷砖,整个二维数组就表示整张地图。为了方便的编辑地图,往往需要开发地图编辑器,所见即所得的编辑地图,将生成的数据存储为文件,在游戏中通过读取文件获取数据。如果场景有多层,比如比较高的建筑物,就要编辑多层地图了。数据组织好了,渲染的时候只要按顺序将二维数组中的每一项对应的Tile在指定位置画出来。实际操作中,地图往往远远超出屏幕大小,这就涉及到卷轴的概念。在卷轴式地图中,屏幕可以看成是一个摄像机,每次只显示地图的一部分。随着主角的移动,屏幕所显示的部分跟着变化,形成卷轴。

卷轴式地图显示的时候,从屏幕(摄像机)所覆盖的第一个Tile开始绘制。需要注意的是屏幕所能覆盖的Tile包括部分覆盖的Tile。上图中第一个Tile就是一个部分覆盖的Tile,尽管只是部分覆盖,它也是需要绘制的第一个Tile,否则屏幕上就会出现没有图像的部分了。造成部分覆盖的原因是屏幕的卷动速度并不是Tile宽度的整数倍。在际开发中,卷轴速度往往可能是变化的,这样的卷轴显得比较自然。按照一定的顺序将屏幕所能覆盖到的所有Tile都绘制一遍,场景的绘制工作就完成了。这些Tile绘制的时候要采用相对于屏幕的坐标,而不是相对于地图的坐标。在2D游戏中经常用到屏幕坐标和地图坐标的转换。我的原则是所有的逻辑计算都统一在地图坐标上,只在最后渲染时才将逻辑坐标转换成屏幕坐标,对于场景和角色都一样。转换的方法很简单。设一个Tile(或角色)在地图上的坐标为(x,y),屏幕相对于地图的坐标为(sx,sy),那么Tile在屏幕上的坐标就是(x-sx,y-sy)。注意单位要统一,往往Tile的单位是格,所以要转换成像素单位,即格子坐标乘上Tile宽度或高度。将到这儿你会发现,其实我们已经实现了TiledLayer的主要功能,再加上管理Tile图片的部分一个简单的Tile引擎就成形了。不过你可千万不要满足,真正的游戏开发中场景引擎比这复杂的多,除了这种2D引擎还有2.5D的斜视角引擎,在渲染速度方面更是做了多方面的优化。

            最后谈谈场景中的物理作用和事件检测。在场景中有些地方是无法通过的,有些地方走上去会触发某个机关,这些信息往往也存储在场景信息中,用另一个数组表示,一般称为物理层、地形层或事件层等等。最常用的信息就是碰撞信息,它用来处理角色和地图的碰撞。碰撞检测在游戏开发中是一个永恒的话题,而2D地图碰撞是其中最简单的一种。由于使用了物理层,我们只要计算出下一帧角色所要到达的位置是哪一格,如果这一格的物理层信息是不可通过,则组织角色前进。这种碰撞检测不同于物体间的碰撞检测,不必和所有的物体进行遍历判断,缺点是不够精确。减小物理层格子的大小可以提高精确度,但这会使数据变多。所以物理层的碰撞检测一般只用在地图上,如果有比较特殊的物体,就让它作为一个角色存在,利用角色间的碰撞检测。

2 角色管理

            角色就是存在于场景中的一切东西,它可以是活动的如RPG中的NPC,也可以是静止的,如一个箱子。角色的数据结构视作用而不同,但基本的都有坐标、速度、使用到的图片等数据。具体到不同的游戏会增加各种数据,比如RPG中,会增加很多角色属性。这里我们只谈最根本的一些东西,主要讲角色的绘制和相互作用。

            绘制角色简单的只是绘制一个帧序列的动画。不同于动画片,角色的动画根据AI变化。不同的AI对应不同的动作和动画。复杂些的角色是由很多部分组合而来,可称之为组合精灵技术,或者称为纸娃娃系统(Avatar)。这种技术可以实现换装效果。组合精灵的复杂之处就在于要组织好每个小部分之间的关系,这需要组合精灵编辑器完成。在专业游戏开发中,会大量运用到各种工具,编写工具也是游戏程序员的工作之一。无论什么样的绘制系统,在最终绘制到屏幕时都要注意两个基本点 - 角色的坐标和层次。角色的坐标是AI运算的结果,绘制时要将地图坐标转换成屏幕坐标。角色的层次需要排序得到,最后根据从远到近的顺序绘制各个角色,这样才能显示正确的遮挡关系。

            角色的物理作用分为和场景之间的物理作用和角色间的物理作用。前者已经在场景管理中讲过。角色间的碰撞检测的时机是某个角色运动时。对于RPG这种类型的游戏,一个矩形碰撞框就可以表示角色的轮廓。对于格斗等类型的游戏,需要用多个碰撞框表示角色。碰撞框的检测非常简单,只要进行矩形相交测试。也有用圆形表示碰撞框的,利用圆心距离和半径的关系就可以判断。在2D飞机类游戏中,三角型的碰撞框也常常使用到。总之用一些几何形状粗略的描述角色的轮廓是2D游戏碰撞检测的通用方法。

            场景中的所有角色往往用一个数组存储起来,这样方便遍历操作。主角做为一个例外,是需要玩家操纵的角色,而其他角色都是用AI控制的。AI虽然是人工智能的缩写,但这里借用来表示操纵角色的代码。

 

            场景和角色是游戏中两个重要的基础组成部分,而开发一个游戏的工作主要部分就是设计各种场景和角色,按照设计编写角色的AI。


 

- 作者: CnXiaowei 2007年08月21日, 星期二 17:32  回复(1) |  引用(1) 加入博采

一些实用的图形用户界面方法

这个函数已反复应用于多个手机应用软件平台
用法:参数定义:str——要分割的字符串
                        font——字体
                        rowMaxW——分割后每行宽度
         支持标示符:
         n    换行
         t     插入两个汉字长度的空格

public static final String[] clipString(String str,Font font,int rowMaxW){
  if(str == null)
   return null;
  if(rowMaxW < font.charWidth('哈'))
   rowMaxW = font.charWidth('哈');         
  int strID = 0;
  int rowW = 0;
  Vector strManager = new Vector();
  char ch = ' ';
  while(str.length() > strID){
   ch = str.charAt(strID);
   switch(ch)
   {
   case 'n':
    strManager.addElement(str.substring(0,strID));
    str = str.substring(strID 1);
    rowW = 0;
    strID = 0;
    break;
   case 't':
    StringBuffer sb = new StringBuffer(str);
    sb.deleteCharAt(strID);
    sb.insert(strID,"       ");
    str = sb.toString();
    break;
   default:
    if(rowW font.charWidth(ch) > rowMaxW){
     strManager.addElement(str.substring(0,strID));
     str = str.substring(strID);
     rowW = 0;
     strID = 0;
    }else{
     rowW = font.charWidth(ch);
     strID ;
    }
   }
  }
  strManager.addElement(str);
  String[] o_Str = new String[strManager.size()];
  strManager.copyInto(o_Str);
  return o_Str;
}



返回结果是一个已切割好的String数组,只要用一个循环打印出来就可以了
public static final void drawClipString(Graphics g,String[] clipStr,Font font,int color,int x,int y){
  if(clipStr == null){
     System.out.println("drawClipString");
     return;
  }
  int FONTH = font.getHeight();
  g.setFont(font);
  g.setColor(color);
  for(int i=0;i<clipStr.length;i )
     g.drawString(clipStr[ i ],x,y i*FONTH,0);
}

参数定义:clipStr——先前分割好的字符串数组
               font——字体
               color——颜色
               x,y——打印的屏幕位置

注意切割和打印函数的字体参数必须保持一致!

半透明技术(限MIDP2.0)

// 获得半透明图片,透明度从0到10共分为11个等级
public static final Image alfImage(Image img,int alf){
  if(img == null){
   System.out.println("alfImage");
   return null;
  }
  if(alf < 0)
   alf = 0;
  else if(alf > 10)
   alf = 10;
  int imgW = img.getWidth();
  int imgH = img.getHeight();
  int[] RGBData = new int[imgW*imgH];
  img.getRGB(RGBData,0,imgW,0,0,imgW,imgH);
  int tmp = ((alf*255/10) << 24)|0x00ffffff;
  for(int i=0;i<RGBData.length;i )
   RGBData &= tmp;
  Image o_Img = Image.createRGBImage(RGBData,imgW,imgH,true);
  return o_Img;
}

 

灰度图转化函数// 得到灰度图
public static final Image grayImage(Image img){
  if(img == null){
   System.out.println("grayAlfImage");
   return null;
  }
  int imgW = img.getWidth();
  int imgH = img.getHeight();
  int[] imgRGBData = new int[imgW*imgH];
  img.getRGB(imgRGBData,0,imgW,0,0,imgW,imgH);
  int ALF = 0;
  int R = 0;
  int G = 0;
  int B = 0;
  int GRAY = 0;
  for(int i=0;i<imgRGBData.length;i ){
   ALF = (imgRGBData >> 24) & 0xFF;
   R = (imgRGBData >> 16) & 0xFF;
   G = (imgRGBData >> 8) & 0xFF;
   B = imgRGBData & 0xFF;
   GRAY = (R*77 G*151 B*28 128)>>8;
   imgRGBData = (ALF<<24)|(GRAY<<16)|(GRAY<<8)|GRAY;
  }
  return Image.createRGBImage(imgRGBData,imgW,imgH,true);
}

- 作者: CnXiaowei 2007年08月21日, 星期二 17:32  回复(1) |  引用(1) 加入博采

手机图片上传的一些小经验

这几天研究j2me图片文件上传,手机上用FileConnection浏览文件夹,读取图片文件,

然后提交到服务器上的http接口.

首先采用的是 HttpConnection ,发送数据(构建文件上传方式也一样), 发现数据都被采用了 chunked 编码,

服务端什么内容都获取不到( 这里遇到一个奇怪的问题,用我本机做服务端,即使chunked编码,也能够完整的获取到数据),

chunked只有在数据超过2016个字节的时候才会产生.

 

于是上网搜集资料,发现mingjava的colala实例采用的是将数据切分为 1700 字节大小,循环上传,直到上传完毕,

(这里不知道mingjava是要做进度故意这么做,还是当时也遇到chunked编码的问题才这么做的.  hh )

还有网上讨论的 http/1.1 http/1.0的问题,好像也是错误的.因为用socket数据,设置http/1.1 http/1.0 结果都是一样的.


图片点击可在新窗口打开查看此主题相关图片如下:

      

      conn = (HttpConnection) Connector.open(server,Connector.READ_WRITE,true);
            conn.setRequestProperty("Content-Type","application/octet-stream");
            conn.setRequestProperty("Content-Length", String.valueOf(data.length));
            conn.setRequestProperty("Connection","Keep-Alive");
            conn.setRequestProperty("Connection","close");
           
            //conn.setRequestProperty("User-Agent", "J2me/Client");
            conn.setRequestMethod(HttpConnection.POST);
            os = conn.openOutputStream();
            os.write(data);

所以只好采用socket,模拟http 协议,上传文件


图片点击可在新窗口打开查看此主题相关图片如下:

            
            StringBuffer sb=new StringBuffer();
            sb.append("POST ");sb.append(URL.query);sb.append(" HTTP/1.1rn");
            sb.append("Host: ");sb.append(URL.host);sb.append("rn");
            sb.append("Content-type: application/octet-streamrn");
            sb.append("Content-Length: ");sb.append(data.length);sb.append("rn");
            sb.append("Connection: closernrn");
           
            sc = (SocketConnection)Connector.open("socket://" URL.host ":" URL.port);
            is = sc.openInputStream();
            os = sc.openOutputStream();
            os.write(sb.toString().getBytes());
            os.write(data);

经过测试成功,构建文件上传数据流格式也没有问题....  这样服务端的程序就和web通常的写法一样了.不需要做特殊处理了


- 作者: CnXiaowei 2007年08月21日, 星期二 17:31  回复(1) |  引用(1) 加入博采

NetBeans移植的解决方案
游戏在不同的机种之间移植的时候,需要修改的地方通常有美术资源,程序片断。针对这些要求,Netbeans使用Configuration来解决这些问题。每个Configuratio里面相关的属性包括:

1、Platform。对应的机型,模拟器,支持的扩展包等。

2、Abilities。该平台一些属性的描述,比如ScreenSize, ScreenColorDepth, JSRXXX等。这些都是用户定义的,用来描述平台的属性。在预处理片断中使用这些属性。

3、Sources Filter。指定该配置下所使用到的程序。如果把资源也放在src目录下的话,也可以用来指定所需要的资源。

4、Libraries & Resources。指定所需要类库和资源。不同机种的资源经常是不同的。

5、Creating JAR。指定JAD和JAR文件的名称。

在程序代码中,就可以使用下面的预处理指示符:

//#if ScreenSize == "128x160"
int tileWidth = 12;
//#elif ScreenSize == "176x208"
int tileWidth = 16;
//#endif

NetBeans会根据Active Configuration把不用的代码注释掉。当使用Build All Project Configurations的时候,就会在dist目录下生成针对每一个Configuration的目录,并把编译好的JAD和JAR文件放到其中。

- 作者: CnXiaowei 2007年08月21日, 星期二 17:31  回复(1) |  引用(1) 加入博采

j2me网络程序中移动资费页面的处理

自中国移动启动上网资费页面提醒后,导致了很多的J2ME联网出现了联不上服务器的问题。即在j2me程序中启动联网后,在有些地区,有些时候,移动网关会把我们的请求截下,返还一个资费提醒的页面给我们,这样我们的服务器将不会收到我们的请求,导致程序出现联网失败的错误。

因此我们在处理网络连接时要加入对移动资费页面的处理,我们可以在发送网络请求后,对返回的response进行http头的检测,如果我们检测到返回的content-type是text/vnd.wap.wml,我们可以扔掉这个response,重新发起一次请求就可以解决资费页面的问题,或者我们也可以提取出资费页面里面的请求地址,再次发送一次请求。

处理的代码如下:

             httpConn = (HttpConnection) Connector.open (url, permissions, true);
           
            String s=httpConn.getHeaderField("Content-Type");
           
            if(s.equals("text/vnd.wap.wml")){

            //重新发起一次请求
                httpConn = (HttpConnection) Connector.open (url, permissions, true);
            }

如果程序本来要返回的类型就是WAP页面,那我们可以对WAP页面的内容进行关键字解析来判断是自己服务器返回的页面还是移动返回的资费提醒页面再进行处理。

- 作者: CnXiaowei 2007年08月21日, 星期二 17:31  回复(2) |  引用(1) 加入博采

手机网络应用客户端软件开发实践简介

 网络应用与客户端软件

      说到移动网络应用,前几年大家首先想到的就是WAP应用。最近随着市场上手机的可编程能力越来越强,手机软件开发平台和产业链的逐渐成熟,手机上的网络应用软件逐渐多了起来,如移动QQ、PICA、掌讯通等等。这些客户端软件凭着丰富的应用、以用户为中心的体验、良好的业务感知度逐渐成为WAP业务之后的又一类重要网络应用。目前的移动软件开发已经逐渐从传统的嵌入式开发中相对独立出来, 主要指手机上的上层应用软件开发,最近也成为了软件行业的新兴热点。

       作为业务运营的手机网络应用客户端软件要求能够部署到大量的手机终端,并注重和网络服务器端业务的结合,目前这方面的开发参考资料还比较少。本文以手机报项目为基础,简单探讨一下手机网络应用客户端软件开发实践中的几个关键问题,希望对新进入者有所帮助。假设我们需要开发一个高可用的手机网络应用客户端软件,用于在线定购和阅读电子报刊业务,覆盖目前移动梦网用户中占有率最高的几十款手机,下面结合KJava开发介绍一下我们的一些实践心得。

用户界面设计

        问题:目前很少有人有手机客户端软件的用户界面(UI)的设计经验,UI设计开发的原则和流程是怎么样的?

         手机客户端软件的UI设计和开发在整个软件开发过程占据相当重要的比重,对于没有相关积累的团队来说,我们估计,软件UI开发占软件全部工作量的40%左右。和其他面向最终用户的软件一样,客户端软件UI设计的原则是:以人为本,保证简单易行的操作方式,同时兼容最大范围手持设备。目前的手机用户界面主要分为两类:通过导航键单手操作方式和触摸屏方式。这两者在操作方式上有着较大区别,但实际项目中如果软件的UI不是太复杂,出于开发成本考虑,UI设计可以主要针对方向键操作的手机,在此基础上再稍做改动以兼容触摸屏手机,这样也是可以接受的。除此之外,手机客户端软件的UI开发还有如下几点经验:

     程序开发人员早期介入

         目前市场占有率较高的手机大部分还只提供KJava开发接口,它的高级UI控件很难满足我们的要求,如果要达到设计的效果一般需要直接使用底层API自己实现。在UI设计开发的流程上,对于没有UI开发经验积累的团队,建议在需求阶段以后先进行原型界面开发,一是为了确认用户的体验需求;二是通过开发人员早期介入确保UI设计人员的设计效果是可以在确定的时间内实现的。第二点很重要,在手机这样一个资源和能力都受限的平台上如果仅仅从UI人员的角度去设计界面,很容易导致无法按时实现或者在真机上的效果太差。UI界面开发阶段一般的流程是这样的:先由UI工程师和开发人员自由讨论,定义出UI元素和大致操作流程,接下来是由开发人员进行实现,最后再由UI人员在已经实现的基础上进行美学创作。

       建议制定一个适合项目实际情况的UI设计开发流程,注意和UI相关的功能一定要在真机上多测试。

   程序界面的有限设计原则

         我们的客户端软件不是浏览器,这点要时刻牢记。客户端软件所能处理的服务器端的内容和业务流程都是相对受限的,也正是因为客户端应用软件对于其他环节的限制要求,才能保证客户端应用相对于浏览器应用更好的用户体验。例如,在实践中无论是服务器传回的内容格式,还是客户端界面层次级别,都是可以要求确定的,其他如软件报刊阅读界面上的字体大小、间距、可下拉屏幕的最大长度等都是可以在需求的时候就确定下来的。

     支持多款手机平台

      问题:KJava平台上的程序离“一次编写,到处运行”还差得很远:不同手机的屏幕大小相差很大,程序需要重新调整界面;不同终端的能力层次不齐;即便是同样的功能,不同型号手机在具体支持程度和方式上也有差别;有些手机终端还有自己特有的BUG。如何让我们的程序支持这几十款手机?

     一般在开发的时候我们先基于SUN公司的标准WTK或者是某款非常典型而且移植性比较好的手机(一般是Nokia)来开发一个基础版本,然后在此基础上按照目标终端的大类做移植,再在大类的基础上做更细的移植,移植的过程如一颗树状展开,最后达到支持所有目标手机终端的目的。以下是一些开发和移植过程中的心得。

     MVC设计模式 模型-视图-控制器(Model-View-Controller,MVC)设计模式及其派生无疑是UI模型的最佳实践,手机应用软件上更是如此。不同手机的屏幕大小差异非常大,在手机客户端应用程序移植的过程中最大的困难就来自于UI界面的移植,MVC设计模式可以很好地使UI界面和程序的数据、控制相分离,从而把后期应用程序的移植这个难题基本控制在界面移植这个范围之内。 MVC设计模式这里就不介绍了,要注意的是整个应用程序大小的限制可能会约束设计模式的实现,即使是最小的类也会使整个应用程序的尺寸增大200字节。实际中可能需要减少类的层次来保持JAR文件在一个合理的大小范围之内,也尽量不要使用单独的类或者匿名类来做控制器,我们的实践中使用一个控制器类来处理所有的业务逻辑,虽然这个类看起来有点臃肿,但是在这种限制条件下,有时候不得不做这种让步。

     建立设备库资料库

       所谓磨刀不误砍材功,对于开发跨平台的应用来说,建立一个目标手机设备资料库非常重要,其中至少要包含我们的应用软件所要用到的各种终端能力特性。网上能查到各种手机终端所支持的Java API等资料,这很方便但是除了屏幕大小外有时候有些参数不可*,而手机设备的一个错误的参数或者BUG会耽误我们开发调试过程中大量的时间。根据我们的客户端应用程序所要用到的终端能力,可以做一个测试程序,用来测试各款手机终端对于这些能力的支持情况,例如:KJava平台的RMS存储限制、最大内存限制、程序所能使用的屏幕大小、支持本地播放的多媒体内容类型、支持网络在线播放的多媒体类型、对联网能力的支持、程序运行时系统对电话呼入的处理以及对Push Registry程序自启动的支持情况和表现等等。我们可以通过自己的测试工具来建立目标终端上的这些属性的资料库,并不断扩充。注意,以真机上运行的结果为准,很多时候模拟器的表现和真机的表现是不一致的,基本上模拟器普遍都存在“缺陷”。

       规范使用资源文件

        为了方便移植,可以将所有的UI界面的图片、提示文字等元素都抽取为资源文件,采用资源文件可以使得资源和代码相分离。在设计阶段注意制定UI元素资源的命名规范,这样移植的时候就可以方便地替换。这种“Skin”的方式,也便于后期方便地更换程序的界面风格。对UI元素资源的规范也有利于UI开发人员的素材积累。

         关注投入产出比

         客户端软件开发面对众多不同的手机平台不是一个理想的平台,因此也很难有一个理想的结果,我们所能做的是在可以接受的范围内寻找一个最佳的投入产出比。按照我们前面提到的移植思路,第一次移植按照程序可以使用的屏幕大小分类,选择五个大类手机进行针对性的开发和移植就可以了,如果一款手机离这五个大类差距都比较大而又不是非常流行的话,不如放弃算了。

        代码移植

        通常我们需要使用一些针对于手机终端特性的程序代码来创建优化的应用程序。例如在只支持Nokia-UI API的手机上播放midi格式声音的方式和支持MMAPI的手机上播放midi格式声音的方式是不同的,后者上可以运行的代码到前者就无法运行。又如某一款手机上通过接口获取的字体高度和实际显示的字体高度是不一致的,同样的代码在不同的手机上就会出现美丑不一的UI界面,这意味着我们不得不在程序中针对终端特性或者缺陷编写一些代码。怎么来完成这个任务?两款手机屏幕的大小相差两倍都不止,如何来做UI界面的代码移植?以下分析几种方法:

        1. 每款手机或者每类手机都各自维护一套代码 这种方法可以最好地发挥程序的性能,也可以避免无用的代码导致编译后的程序大小膨胀。但是将来如果要添加一个新的程序功能,就不得不在每套代码中都实现一遍。后期的版本跟踪和维护是一件非常困难的事情。

         2. 利用编译器优化来调整代码 通过使用带有static final 变量的if else 语句,Java编译器在编译的时候可以去除那些永远也不可能被执行到的代码。例如使用if (Configuration.IS_SUPPORT_XXXX),来表示后面是针对支持XXXX特性的手机的代码,否则编译时这段代码就会被精简掉。这个方法不错,但是每款手机都要维护一个Configuration源代码文件,随着支持设备的增多,某些情况下又要对Configuration文件进行分类合并,另外一个问题是,不能“动态”地import类,也不能“动态”使用类变量类方法,在开发过程中的开发环境不能针对于某个具体的手机类型。

         3. 第三方预编译工具 这种方法和上面一种方法有点类似,在代码中加入判断的预编译指令,所不同的是它在程序编译之前就对代码做了修改,这样可以实现“动态”地使用类和类的变量、方法。这种方法对于每一款手机也需要一个设备的配置文件,里面描述我们关注的一些设备的能力和参数,和上面的方法不同的是它的配置文件不需要作为源代码导入,可以保存为XML格式的文件,通过将各种手机终端设备配置文件汇聚起来,就是前面提到的设备资料库,一般是XML形式组织,随着项目的进行代码管理的复杂度不会增加。我们实践中使用的一个第三方工具J2MEPolish就是使用了使用预处理标记来区分不同手机的代码,使用预处理器对代码进行处理,使得其生成针对某一款或某一类机型的特定代码,然后再由编译器进行编译。

          智能网络客户端

           手机的网络条件目前还是不乐观的,一方面是速度慢,另一方因为手机信号原因,不能保证网络的稳定,用户的一次网络交互可能需要等待很长时间返回或者根本不会返回。如果采用浏览器的方式,必然导致用户体验的大幅下降。客户端之所以最近会兴起,其中一个原因就是客户端可以更智能,可以在离线和在线状态之间切换,并且在离线情况下仍然有一定的可用性。要改善用户的移动网络业务的体验必须要在这个环节下功夫。

            微软的Smart Client架构是这方面一个非常强大的架构,在我们的手机报实践中,借鉴了Smart Client的思路和部分设计模式,使得用户的网络服务请求和界面操作独立开来,通过合理的服务调用流程,可以让用户获得最佳的体验:用户无需花时间等待任何网络服务请求操作,在网络服务器请求完成以后以最合理的方式通知用户而不打扰用户使用手机报。基于客户端的应用还有一个好处就是可以接收服务器Push的消息,这也使它看起来更“智能”。可以通短信等方式将客户端软件远程唤醒并传递消息。这些功能都是传统浏览器应用所不具备的。可以预见,服务器端也需要针对于智能客户端的新特性提供新功能。

            下面列出一些智能客户端环节需要关注的一些问题:

           如何缓存用户操作数据,以应对网络异常,并保证用户体验?

          用户的网络操作返回以后,如何判断是否要提醒用户?

          如何检测网络状态,以改变客户端的工作状态?

          如何针对运营商网络优化应用层通信协议,选择数据包的大小,保证通讯效率的同时保证成功率?

      小结

        前面简单介绍一下基于KJava的手机网络应用客户端软件开发实践中的几个关键问题上的心得,在实际程序开发、调试过程中还有很多关于开发环境、各种终端以及网络的非常规的问题,只能*自己在实践中去体会。另外因为KJava平台和手机终端本身的限制,有些问题在上层应用开发层面是没有办法解决的。最近“智能手机”的兴起,大多给了开发者提供了除JavaME平台以外的选择,发挥的舞台也更大,将来的趋势也是手机的可开发性越来越好,限制越来越少,但目前的移动终端和移动网络相比于PC和互联网都是相当受限的。

         回到手机的网络应用上来看,客户端软件可以提供更好的用户体验,但是和服务器还是一个整体,一般业务的核心是还都是在服务器端。在客户端基本功能完善以后,剩下的就是如何完善针对于客户端应用的服务器的功能,这一块相比之下更值得挖掘,意义更大。我相信这是未来最具潜力的软件架构之一,基于客户端的移动互联网应用才刚刚拉开帷幕。

- 作者: CnXiaowei 2007年08月21日, 星期二 17:30  回复(1) |  引用(1) 加入博采

手机网络应用客户端软件开发实践简介

 网络应用与客户端软件

      说到移动网络应用,前几年大家首先想到的就是WAP应用。最近随着市场上手机的可编程能力越来越强,手机软件开发平台和产业链的逐渐成熟,手机上的网络应用软件逐渐多了起来,如移动QQ、PICA、掌讯通等等。这些客户端软件凭着丰富的应用、以用户为中心的体验、良好的业务感知度逐渐成为WAP业务之后的又一类重要网络应用。目前的移动软件开发已经逐渐从传统的嵌入式开发中相对独立出来, 主要指手机上的上层应用软件开发,最近也成为了软件行业的新兴热点。

       作为业务运营的手机网络应用客户端软件要求能够部署到大量的手机终端,并注重和网络服务器端业务的结合,目前这方面的开发参考资料还比较少。本文以手机报项目为基础,简单探讨一下手机网络应用客户端软件开发实践中的几个关键问题,希望对新进入者有所帮助。假设我们需要开发一个高可用的手机网络应用客户端软件,用于在线定购和阅读电子报刊业务,覆盖目前移动梦网用户中占有率最高的几十款手机,下面结合KJava开发介绍一下我们的一些实践心得。

用户界面设计

        问题:目前很少有人有手机客户端软件的用户界面(UI)的设计经验,UI设计开发的原则和流程是怎么样的?

         手机客户端软件的UI设计和开发在整个软件开发过程占据相当重要的比重,对于没有相关积累的团队来说,我们估计,软件UI开发占软件全部工作量的40%左右。和其他面向最终用户的软件一样,客户端软件UI设计的原则是:以人为本,保证简单易行的操作方式,同时兼容最大范围的手持设备。目前的手机用户界面主要分为两类:通过导航键单手操作方式和触摸屏方式。这两者在操作方式上有着较大区别,但实际项目中如果软件的UI不是太复杂,出于开发成本考虑,UI设计可以主要针对方向键操作的手机,在此基础上再稍做改动以兼容触摸屏手机,这样也是可以接受的。除此之外,手机客户端软件的UI开发还有如下几点经验:

     程序开发人员早期介入

         目前市场占有率较高的手机大部分还只提供KJava开发接口,它的高级UI控件很难满足我们的要求,如果要达到设计的效果一般需要直接使用底层API自己实现。在UI设计开发的流程上,对于没有UI开发经验积累的团队,建议在需求阶段以后先进行原型界面开发,一是为了确认用户的体验需求;二是通过开发人员早期介入确保UI设计人员的设计效果是可以在确定的时间内实现的。第二点很重要,在手机这样一个资源和能力都受限的平台上如果仅仅从UI人员的角度去设计界面,很容易导致无法按时实现或者在真机上的效果太差。UI界面开发阶段一般的流程是这样的:先由UI工程师和开发人员自由讨论,定义出UI元素和大致操作流程,接下来是由开发人员进行实现,最后再由UI人员在已经实现的基础上进行美学创作。

       建议制定一个适合项目实际情况的UI设计开发流程,注意和UI相关的功能一定要在真机上多测试。

   程序界面的有限设计原则

         我们的客户端软件不是浏览器,这点要时刻牢记。客户端软件所能处理的服务器端的内容和业务流程都是相对受限的,也正是因为客户端应用软件对于其他环节的限制要求,才能保证客户端应用相对于浏览器应用更好的用户体验。例如,在实践中无论是服务器传回的内容格式,还是客户端界面层次级别,都是可以要求确定的,其他如软件报刊阅读界面上的字体大小、间距、可下拉屏幕的最大长度等都是可以在需求的时候就确定下来的。

     支持多款手机平台

      问题:KJava平台上的程序离“一次编写,到处运行”还差得很远:不同手机的屏幕大小相差很大,程序需要重新调整界面;不同终端的能力层次不齐;即便是同样的功能,不同型号手机在具体支持程度和方式上也有差别;有些手机终端还有自己特有的BUG。如何让我们的程序支持这几十款手机?

     一般在开发的时候我们先基于SUN公司的标准WTK或者是某款非常典型而且移植性比较好的手机(一般是Nokia)来开发一个基础版本,然后在此基础上按照目标终端的大类做移植,再在大类的基础上做更细的移植,移植的过程如一颗树状展开,最后达到支持所有目标手机终端的目的。以下是一些开发和移植过程中的心得。

     MVC设计模式 模型-视图-控制器(Model-View-Controller,MVC)设计模式及其派生无疑是UI模型的最佳实践,手机应用软件上更是如此。不同手机的屏幕大小差异非常大,在手机客户端应用程序移植的过程中最大的困难就来自于UI界面的移植,MVC设计模式可以很好地使UI界面和程序的数据、控制相分离,从而把后期应用程序的移植这个难题基本控制在界面移植这个范围之内。 MVC设计模式这里就不介绍了,要注意的是整个应用程序大小的限制可能会约束设计模式的实现,即使是最小的类也会使整个应用程序的尺寸增大200字节。实际中可能需要减少类的层次来保持JAR文件在一个合理的大小范围之内,也尽量不要使用单独的类或者匿名类来做控制器,我们的实践中使用一个控制器类来处理所有的业务逻辑,虽然这个类看起来有点臃肿,但是在这种限制条件下,有时候不得不做这种让步。

     建立设备库资料库

       所谓磨刀不误砍材功,对于开发跨平台的应用来说,建立一个目标手机设备资料库非常重要,其中至少要包含我们的应用软件所要用到的各种终端能力特性。网上能查到各种手机终端所支持的Java API等资料,这很方便但是除了屏幕大小外有时候有些参数不可*,而手机设备的一个错误的参数或者BUG会耽误我们开发调试过程中大量的时间。根据们的客户端应用程序所要用到的终端能力,可以做一个测试程序,用来测试各款手机终端对于这些能力的支持情况,例如:KJava平台的RMS存储限制、最大内存限制、程序所能使用的屏幕大小、支持本地播放的多媒体内容类型、支持网络在线播放的多媒体类型、对联网能力的支持、程序运行时系统对电话呼入的处理以及对Push Registry程序自启动的支持情况和表现等等。我们可以通过自己的测试工具来建立目标终端上的这些属性的资料库,并不断扩充。注意,以真机上运行的结果为准,很多时候模拟器的表现和真机的表现是不一致的,基本上模拟器普遍都存在“缺陷”。

       规范使用资源文件

        为了方便移植,可以将所有的UI界面的图片、提示文字等元素都抽取为资源文件,采用资源文件可以使得资源和代码相分离。在设计阶段注意制定UI元素资源的命名规范,这样移植的时候就可以方便地替换。这种“Skin”的方式,也便于后期方便地更换程序的界面风格。对UI元素资源的规范也有利于UI开发人员的素材积累。

         关注投入产出比

         客户端软件开发面对众多不同的手机平台不是一个理想的平台,因此也很难有一个理想的结果,我们所能做的是在可以接受的范围内寻找一个最佳的投入产出比。按照我们前面提到的移植思路,第一次移植按照程序可以使用的屏幕大小分类,选择五个大类手机进行针对性的开发和移植就可以了,如果一款手机离这五个大类差距都比较大而又不是非常流行的话,不如放弃算了。

        代码移植

        通常我们需要使用一些针对于手机终端特性的程序代码来创建优化的应用程序。例如在只支持Nokia-UI API的手机上播放midi格式声音的方式和支持MMAPI的手机上播放midi格式声音的方式是不同的,后者上可以运行的代码到前者就无法运行。又如某一款手机上通过接口获取的字体高度和实际显示的字体高度是不一致的,同样的代码在不同的手机上就会出现美丑不一的UI界面,这意味着我们不得不在程序中针对终端特性或者缺陷编写一些代码。怎么来完成这个任务?两款手机屏幕的大小相差两倍都不止,如何来做UI界面的代码移植?以下分析几种方法:

        1. 每款手机或者每类手机都各自维护一套代码 这种方法可以最好地发挥程序的性能,也可以避免无用的代码导致编译后的程序大小膨胀。但是将来如果要添加一个新的程序功能,就不得不在每套代码中都实现一遍。后期的版本跟踪和维护是一件非常困难的事情。

         2. 利用编译器优化来调整代码 通过使用带有static final 变量的if else 语句,Java编译器在编译的时候可以去除那些永远也不可能被执行到的代码。例如使用if (Configuration.IS_SUPPORT_XXXX),来表示后面是针对支持XXXX特性的手机的代码,否则编译时这段代码就会被精简掉。这个方法不错,但是每款手机都要维护一个Configuration源代码文件,随着支持设备的增多,某些情况下又要对Configuration文件进行分类合并,另外一个问题是,不能“动态”地import类,也不能“动态”使用类变量类方法,在开发过程中的开发环境不能针对于某个具体的手机类型。

         3. 第三方预编译工具 这种方法和上面一种方法有点类似,在代码中加入判断的预编译指令,所不同的是它在程序编译之前就对代码做了修改,这样可以实现“动态”地使用类和类的变量、方法。这种方法对于每一款手机也需要一个设备的配置文件,里面描述我们关注的一些设备的能力和参数,和上面的方法不同的是它的配置文件不需要作为源代码导入,可以保存为XML格式的文件,通过将各种手机终端设备配置文件汇聚起来,就是前面提到的设备资料库,一般是XML形式组织,随着项目的进行代码管理的复杂度不会增加。我们实践中使用的一个第三方工具J2MEPolish就是使用了使用预处理标记来区分不同手机的代码,使用预处理器对代码进行处理,使得其生成针对某一款或某一类机型的特定代码,然后再由编译器进行编译。

          智能网络客户端

           手机的网络条件目前还是不乐观的,一方面是速度慢,另一方因为手机信号原因,不能保证网络的稳定,用户的一次网络交互可能需要等待很长时间返回或者根本不会返回。如果采用浏览器的方式,必然导致用户体验的大幅下降。客户端之所以最近会兴起,其中一个原因就是客户端可以更智能,可以在离线和在线状态之间切换,并且在离线情况下仍然有一定的可用性。要改善用户的移动网络业务的体验必须要在这个环节下功夫。

            微软的Smart Client架构是这方面一个非常强大的架构,在我们的手机报实践中,借鉴了Smart Client的思路和部分设计模式,使得用户的网络服务请求和界面操作独立开来,通过合理的服务调用流程,可以让用户获得最佳的体验:用户无需花时间等待任何网络服务请求操作,在网络服务器请求完成以后以最合理的方式通知用户而不打扰用户使用手机报。基于客户端的应用还有一个好处就是可以接收服务器Push的消息,这也使它看起来更“智能”。可以通过短信等方式将客户端软件远程唤醒并传递消息。这些功能都是传统浏览器应用所不具备的。可以预见,服务器端也需要针对于智能客户端的新特性提供新功能。

            下面列出一些智能客户端环节需要关注的一些问题:

           如何缓存用户操作数据,以应对网络异常,并保证用户体验?

          用户的网络操作返回以后,如何判断是否要提醒用户?

          如何检测网络状态,以改变客户端的工作状态?

          如何针对运营商网络优化应用层通信协议,选择数据包的大小,保证通讯效率的同时保证成功率?

      小结

        前面简单介绍一下基于KJava的手机网络应用客户端软件开发实践中的几个关键问题上的心得,在实际程序开发、调试过程中还有很多关于开发环境、各种终端以及网络的非常规的问题,只能*自己在实践中去体会。另外因为KJava平台和手机终端本身的限制,有些问题在上层应用开发层面是没有办法解决的。最近“智能手机”的兴起,大多给了开发者提供了除JavaME平台以外的选择,发挥的舞台也更大,将来的趋势也是手机的可开发性越来越好,限制越来越少,但目前的移动终端和移动网络相比于PC和互联网都是相当受限的。

         回到手机的网络应用上来看,客户端软件可以提供更好的用户体验,但是和服务器还是一个整体,一般业务的核心是还都是在服务器端。在客户端基本功能完善以后,剩下的就是如何完善针对于客户端应用的服务器的功能,这一块相比之下更值得挖掘,意义更大。我相信这是未来最具潜力的软件架构之一,基于客户端的移动互联网应用才刚刚拉开帷幕。

- 作者: CnXiaowei 2007年08月21日, 星期二 17:27  回复(1) |  引用(1) 加入博采

#title#
#content#

- 作者: CnXiaowei 2007年08月21日, 星期二 17:05  回复(1) |  引用(1) 加入博采

自做代码生成器(五)举例实践
【摘要】
    整个代码生成工具开发完成,接下来就是如何利用它节省时间了,这个工具的使用有几种方式,可以在数据库表已经建好以后,生成数据库的xml文件,也可以先手动编写数据表的xml文件,再生成sql语句创建数据表。
【全文】

    我们先定义一个数据表的xml文件,比如系统用户的表system_user,定义的xml文件如下:

system_user.xml


<?xml version="1.0" encoding="GB2312"?>
<table>
 <name>system_user</name>
 <class-name>SystemUser</class-name>
 <short-name>systemUser</short-name>
 <display>系统用户</display>
 <fields>
  <field>
   <label>用户名</label>
   <name>user_name</name>
   <class-name>userName</class-name>
   <type>VARCHAR2</type>
   <class-type>String</class-type>
   <length>20</length>
   <null>0</null>
   <readonly>false</readonly>
   <form-type>text</form-type>
  </field>
  <field>
   <label>密码</label>
   <name>user_password</name>
   <class-name>userPassword</class-name>
   <type>VARCHAR2</type>
   <class-type>String</class-type>
   <length>20</length>
   <null>0</null>
   <readonly>false</readonlygt;
   <form-type>text</form-type>
  </field>
  <field>
   <label>用户姓名</label>
   <name>user_realname</name>
   <class-name>userRealName</class-name>
   <type>VARCHAR2</type>
   <class-type>String</class-type>
   <length>50</length>
   <null>0</null>
   <readonly>false</readonly>
   <form-type>text</form-type>
  </field>
 </fields>
 <pks>
  <pk>
   <name>user_name</name>
  </pk>
 </pks>
</table>

    这个文件的生成其实可以做成有界面配置的,速度会快一些,只是有些懒所以还没做出来,所以还是得手写了。

    定义好这个数据表,我们先要生成sql语句,生成sql语句的模板文件如下:

create_table.vm

##创建表的SQL语句
#foreach ($table in $list)
##各个字段
#set ($fields = $table.getTableFields())
#set ($pks = $table.getPrimaryKeys())
/*
 创建数据表$table.TableName.toUpperCase()
*/
CREATE TABLE $table.TableName.toUpperCase()
(
#foreach ($field in $fields)
 $field.FieldName.toUpperCase() $field.FieldType.toUpperCase()#if($field.FieldType.toUpperCase().equals("NUMBER") || $field.FieldType.toUpperCase().equals("VARCHAR2") || $field.FieldType.toUpperCase().equals("CHAR"))($field.FieldLen)#end#if($field.FieldNull == 0) NOT NULL#end,  -- $field.FieldLabel
#end
 CONSTRAINT PK_$table.TableName.toUpperCase() PRIMARY KEY (#set($c=0)#foreach($pk in $pks)#if($c!=0),#set($c=1)#end$pk.FieldName.toUpperCase()#end)
);
#foreach($field in $fields)
COMMENT ON COLUMN ${table.TableName.toUpperCase()}.$field.FieldName.toUpperCase() IS '$field.FieldLabel';
#end

#end

#foreach($table in $object)
#set ($fields = $table.getTableFields())
#set ($pks = $table.getPrimaryKeys())
#foreach ($field in $fields)
#if(!$field.FieldSeq.equals(""))
CREATE SEQUENCE $table.TableName.toUpperCase()_SEQ;
#end
#end
#end

    接着需要编写创建sql文件的配置文件:

make_sql.xml


<?xml version="1.0" encoding="GB2312"?>
<auto-coder comment="代码生成器配置文件,创建数据表以及生成java代码">
 <file-path comment="生成代码保存的路径">code\sql</file-path>
 <database comment="数据库配置">
  <driver>oracle.jdbc.driver.OracleDriver</driver>
  <url>jdbc:oracle:thin:@192.168.0.186:1521:testdb</url>
  <username>wap</username>
  <password>a123</password>
  <dbflag comment="是否从数据库读取">false</dbflag>
 </database>
 <templates comment="单个数据表模板">
 </templates>
 <table-templates comment="多个数据表模板">
  <template comment="生成建表SQL">
   <template-type>other</template-type>
   <template-file>templates\create_table.vm</template-file>
   <java-package></java-package>
   <class-name>create_table.sql</class-name>
  </template> 
 </table-templates>
 <table-file comment="要生成的数据表文件">tables.txt</table-file>
</auto-coder>

    这样,几个配置文件都编写完毕,就可以生成代码了,生成的代码如下:

/

*
 创建数据表SYSTEM_USER
*/
CREATE TABLE SYSTEM_USER
(
 USER_NAME VARCHAR2(20) NOT NULL,  -- 用户名
 USER_PASSWORD VARCHAR2(20) NOT NULL,  -- 密码
 USER_REALNAME VARCHAR2(50) NOT NULL,  -- 用户姓名
 CONSTRAINT PK_SYSTEM_USER PRIMARY KEY (USER_NAME)
);
COMMENT ON COLUMN SYSTEM_USER.USER_NAME IS '用户名';
COMMENT ON COLUMN SYSTEM_USER.USER_PASSWORD IS '密码';
COMMENT ON COLUMN SYSTEM_USER.USER_REALNAME IS '用户姓名';

这里生成的sql可以直接运行创建数据库表。

除了这个,还可以生成java代码,甚至html页面。

[点这里下载例子]

- 作者: CnXiaowei 2006年08月6日, 星期日 00:15  回复(2) |  引用(1) 加入博采