欢迎访问wenhsuan的技术博客:Linux & Embedded
gdb中list命令用不了的问题
学习嵌入式Linux系统的笔记和体会(转)
VMware与Redhat的网络设置兼容性
VMware与Redhat的网络设置兼容性
VMware4.5以上和Redhat 9以上之间,网卡驱动有些不兼容:
Redhat 9.0作Guest OS时,激活虚拟网卡时,总是提示
诸如 "Determining IP information for eth0... failed; no link present. Check cable?"
原因貌似是VMware提供的虚拟网卡驱动有一点点问题,可以通过下面方法解决:
以root权限,编辑 /etc/sysconfig/network-scripts/ifcfg-eth0和
/etc/sysconfig/networking/devices/ifcfg-eth0
在每个文件中添加:
check_link_down () {
return 1;
}
然后激活虚拟网卡eth0 应该就可以了
另外,VMware的几种网络模式,选择NAT (网络地址转换模式) 就行了,
这个对于让Guest OS访问Internet是最简单的,对应"VMnet8"
什么叫网关的精解(超经典)
转自(协议分析论坛)
计算机主机网关的作用是什么?
假设你的名字叫小不点,你住在一个大院子里,你的邻居有很多小伙伴,在门口传达室还有个看大门的李大爷,李大爷就是你的网关。当你想跟院子里的某个小伙伴玩,只要你在院子里大喊一声他的名字,他听到了就会回应你,并且跑出来跟你玩。
但是你不被允许走出大门,你想与外界发生的一切联系,都必须由门口的李大爷(网关)用电话帮助你联系。假如你想找你的同学小明聊天,小明家住在很远的另外一个院子里,他家的院子里也有一个看门的王大爷(小明的网关)。但是你不知道小明家的电话号码,不过你的班主任老师有一份你们班全体同学的名单和电话号码对照表,你的老师就是你的DNS服务器。于是你在家里拨通了门口李大爷的电话,有了下面的对话:
小不点:李大爷,我想找班主任查下小明的电话号码行吗?
李大爷:好,你等着。(接着李大爷给你的班主任挂了一个电话,问清楚
了小明的电话)问到了,他家的号码是211.99.99.99
小不点:太好了!李大爷,我想找小明,你再帮我联系一下小明吧。
李大爷:没问题。(接着李大爷向电话局发出了请求接通小明家电话的请
求,最后一关当然是被转接到了小明家那个院子的王大爷那里,然后王大
爷把电话给转到小明家)
就这样你和小明取得了联系。
至于DHCP服务器嘛,可以这样比喻:
你家院子里的居民越来越多了,传达室李大爷那里的电话交换机已经不能满足这么多居民的需求了,所以只好采用了一种新技术叫做DHCP,居民们开机的时候随机得到一个电话号码,每一次得到的号码都可能会不同。
你家门口的李大爷:就是你的网关
你的班主任:就是你的DNS服务器
传达室的电话交换机:就是你的DHCP服务器
同上,李大爷和王大爷之间的对话就叫做路由。
另:如果还有个小朋友叫做小暗,他住的院子看门的是孙大爷,因为小暗的院子刚盖好,孙大爷刚来不久,他没有李大爷和王大爷办公室的电话(李大爷和王大爷当然也没有他的电话),这时会有两种情况:
1、居委会的赵大妈告诉了孙大爷关于李、王两位大爷的电话(同时赵大妈也告诉了李、王关于孙的电话),这就叫静态设定路由
2、赵大妈病了,孙大爷自己到处打电话,见人就说:“我是小暗他们院子管电话的”,结果被李、王二位听到了,就记在了他们的通讯录上,然后李、王就给孙大爷回了个电话说:“我是小明(小不点)他们院子管电话的”,这就叫动态设定路由
然后有一天小不点要找小暗,结果自然是小不点给李大爷打电话说:“大爷,我找小暗”(这里省略了李大爷去查小暗电话的过程,假设他知道小暗的电话),李大爷一找通讯录:“哦,小暗的院子的电话是孙大爷管着的,要找小暗自然先要通知孙大爷,我可以通知王大爷让他去找孙大爷,也可以自己直接找孙,那当然是自己直接找孙方便了”,于是李大爷给孙大爷打了电话,然后孙大爷又把电话转到了小暗家。
这里李大爷的通讯录叫做路由表。
李大爷选择是自己直接找孙大爷还是让王大爷帮忙转接叫做路由选择。
李大爷之所以选择直接找孙大爷是有依据的,因为他直接找孙大爷就能一步到位,如果要王大爷转接就需要两步才能完成,这里的“步”叫做“跳数”,李大爷的选择遵循的是最少步骤(跳数)原则(如果他不遵守这个原则,小不点可能就会多等些时间才能找到小暗,最终结果可能导致李大爷因工作不力被炒鱿鱼,这叫做“延时太长,选路原则不合理,换了一个路由器”)
当然,事情总是变化的,小不点和小明吵架了,这些天小不点老是给小暗打电话,小明心里想:“操,他是不是在说我坏话啊?”于是小明决定偷听小不点和小暗的通话,但是他又不能出院子,怎么办呢?小明做了这样一个决定:
首先他告诉自己院里管电话的王大爷说:“你给李大爷打个电话说小暗搬到咱们院子了,以后凡是打给他的电话我来接”,王大爷没反映过来(毕竟年纪大了啊!)就给李大爷打了电话,说:“现在我来管理小暗的电话了,孙已经不管了”,结果李大爷就把他的通讯录改了,这叫做路由欺骗。
以后小不点再找小暗,李大爷就转给王大爷了(其实应该转给孙大爷的),王大爷收到了这个电话就转给了小明(因为他之前已经和小明说好了),小明收到这个电话就假装小暗和小不点通信。因为小明作贼心虚,害怕明天小不点和小暗见面后当面问他,于是通信断了之后,又自己以小不点的名义给小暗通了个电话复述了一遍刚才的话,有这就叫数据窃听
再后来,小不点还是不断的和小暗联系,而零落了小明,小明心里嘀咕啊:“我不能总是这样以小暗的身份和小不点通话啊,外一有一天露馅了怎么办!”于是他想了一个更阴险的招数:“干脆我也不偷听你们的电话了,你小不点不是不给我打电话吗!那我让你也给小暗打不了,哼哼!”,他怎么做的呢?我们来看:
他联系了一批狐朋狗友,和他们串通好,每天固定一个时间大家一起给小暗院子传达室打电话,内容什么都有,只要传达室的孙爷爷接电话,就会听到“打雷啦,下雨收衣服啊!”、“人是人他妈生的,妖是妖他妈生的”、“你妈贵姓”等等,听的脑袋都大了,不听又不行,电话不停的响啊!终于有一天,孙爷爷忍不住了,大喊一声:“我受不了拉!!!!”,于是上吊自杀了!
这就是最简单的DDOS攻击,孙爷爷心理承受能力弱的现象叫做“数据报处理模块有BUG”,孙爷爷的自杀叫做“路由器瘫痪”。如果是我,就会微笑着和他们拉家常,例如告诉他们“我早就听了天气预报,衣服10分钟前已经收好了”或者“那你妈是人还是妖”或者“和你奶奶一个姓”等等,我这种健全的心理叫做“健壮的数据报处理,能够抵御任何攻击”
孙爷爷瘫了之后,小不点终于不再给小暗打电话了,因为无论他怎么打对方都是忙音,这种现象叫做“拒绝服务”,所以小明的做法还有一个名字叫做“拒绝服务攻击”。
小明终于安静了几天,...
几天后,小明的院子来了一个美丽的女孩,名字叫做小丽,小明很喜欢她(小小年纪玩什么早恋!)可是小丽有个很帅的男朋友,小明干瞪眼没办法。当然这里还是要遵循上面的原则:小丽是不能出院子的。那个男的想泡小丽自然只能打电话,于是小明又蠢蠢欲动了:
还记得王爷爷是院子的电话总管吗?他之所以能管理电话是因为他有一个通讯录,因为同一个院子可能有2个孩子都叫小明,靠名字无法区分,所以通讯录上每一行只有两项:
门牌 电话
一号门 1234567 (这个是小明的)
二号门 7654321 (这个是小丽的)
......
王爷爷记性不好,但这总不会错了吧(同一个院子不会有2个“二号门”吧)?每次打电话人家都要说出要找的电话号码,然后通过通讯录去院子里面敲门,比如人家说我找“1234567”,于是王爷爷一比较,哦,是一号门的,他就去敲一号门“听电话”,如果是找“7654321”,那他就找二号门“听电话”。
这里的电话号码就是传说中的“IP地址”
这里的门牌号就是传说中的网卡的’MAC‘地址(每一块网卡的MAC地址都是不一样的,这是网卡的制造商写死在网卡的芯片中的)
小明心里想“奶奶的,老子泡不到你也别想泡”,于是他打起了王爷爷通讯录的主意,经过细心的观察,周密的准备,他终于发现王爷爷有尿频的毛病(毕竟是老人啊...),终于在一个月黑风高的白天,王爷爷去上厕所了,小明偷偷的摸进传达室,小心翼翼的改了王爷爷的通讯录......
过了几天,小丽的男朋友又给小丽打来了电话,对方报的电话是“7654321”,王爷爷一看通讯录,靠:
门牌 电话
一号门 1234567 (这个是小明的)
一号门 7654321 (注意:这个原来是小丽的,但是被小明改了)
......
王爷爷不知道改了啊,于是就去找一号门的小明了,小明心里这个美啊,他以小丽父亲的口吻严厉的教训了那个男的和小丽之间不正当的男女关系,结果那个男的恭恭敬敬的挂了电话。当然小丽并不知道整个事情的发生...
这里小明的行为叫做“ARP欺骗”(因为在实际的网络上是通过发送ARP数据包来实现的,所以叫做“ARP欺骗”),王爷爷的通讯录叫做“ARP表”
这里要注意:王爷爷现在有两个通讯录了,一个是记录每个院子传达室电话的本本,叫做“路由表”,一个是现在说的记录院子里面详细信息的本本,叫做“ARP表”。
有句命言是“人们总是在追求完美的,尽管永远也做不到”(请记住这句话,因为这是一个大名人--也就是我,说的)
王爷爷的制度中有一条是这么写的“每个月要重新检查一下门牌号和电话的对应本(也就是ARP表)”,这个动作叫做“刷新ARP表”,每个月的时间限制叫做“刷新ARP表的周期”。这样小明为了让那个男的永远不能找到小丽,之后每个月都要偷偷改一次那个通讯录,不过这样也是不得不做的事啊!
补充一点,小明是很聪明的,如果通讯录(ARP表)被改成了这样:
门牌(MAC) 电话(IP)
一号门 1234567 (这个是小明的)
二号门 1234567 (注意:这个被小明改了,但是他一时头晕改错了)
......
就会是计算机就会弹出一个对话框提示“出现重复的IP地址”,最终会导致王爷爷不知所措,于是通知一号门和二号门,你们的电话重复了。这样小丽就知道有人在破坏她的好事,这个现象叫做“骗局被揭穿了”
小不点知道了小明偷听他和小暗的电话,于是就和小暗约定好了密码。小不点在家里把要说的加密了之后告诉小暗。土豆-〉星期三,地瓜-〉请客,笨蛋-〉小不点家。于是小不点告诉小暗:土豆笨蛋地瓜。小明听了???不懂。。。。郁闷了。。。这是加密。
除此之外,小丽也知道了小明改他家的电话号码了。于是王爷爷就登门一个一个把电话和门牌号记下来。并且藏起来不允许外人修改,只能自己有钥匙(密码)。这是ip地址和MAC地址绑定。当有人改了电话号码的时候,就得找王爷爷改。麻烦是麻烦了,但是安全了。不过小明偷偷的把王爷爷的钥匙偷配了一把(盗窃密码成功),于是他还可以修改。这样么,就这样了。
Windows CE.NET进程、线程和调度管理
Windows CE.NET是一个抢占多任务操作系统,在调度过程中,内核的调度系统包含一个当前所有进程中线程的优先级列表,并对所有的线程按优先级排列顺序。当中断发生时,调度系统重新安排所有线程的排列顺序。
一个进程是一个正运行的应用程序的实例。它由两个部分组成:一个是操作系统用来管理这个进程的内核对象。另一个是这个进程拥有的地址空间。这个地址空间包含应用程序的代码段、静态数据段、堆、栈,非XIP(Execute In Place)DLL。从执行角度方面看,一个进程由一个或多个线程组成。一个线程是一个执行单元,它控制CPU执行进程中某一段代码段。一个线程可以访问这个进程中所有的地址空间和资源。线程是Windows CE操作系统分配CPU时间的基本单位。一个进程最少包括一个线程来执行代码,这个线程又叫做主线程。
Windows CE.NET最多支持32个进程同时运行。这是由整个系统分配给所有进程的总地址空间决定的。低于Windows CE 4.0版本(也就是低于.NET的版本)的CE操作系统,总进程空间从0x0000 0000到0x4200 0000 ,每32MB地址空间为一个槽(Slot),共33个槽。当一个进程启动时,内核选择一个没有被占用的槽作为这个进程的地址空间。其中0x0000 0000到0x01FF FFFF这个槽称为Slot 0。每个进程在即将得到CPU控制权时,将整个地址映射到Slot 0[7,12]。
线程有五中状态,分别为运行、挂起、睡眠、阻塞、终止。当所有线程全部处于阻塞状态时,内核处于空闲模式(Idle mode),这时对CPU的电力供应将减小。
Windows CE.NET不像其他Windows操作系统将进程分为不同的优先级类,Windows CE.NET只将线程分为256个优先级。0优先级最高,255最低。在需要时进程创建线程,附加线程的数量只受限于设备中RAM的可用容量。在Windows CE中,通过CreateThread()函数可以创建线程,通过GetThreadPriority()和SetThreadPriority()可以获得和改变线程优先级。挂起一个线程使用SuspendThread()函数。恢复线程使用ResumeThread()函数。
同时Windows CE提供了线程同步机制,线程同步的有些解决办法运行在用户模式,有些运行在内核模式。互锁和临界区运行在用户模式,事件对象,互斥对象运行在内核模式。同步机制还包括信号,消息队列等。
WinCE下的多线程
一、创建进程
线程由两部分构成.线程的内核对象和线程堆栈
HANDLE handle1;
static DWORD ThreadProc(PVOID pArg);
CListBox * pLstOne;
DWORD dwThreadId1;
handle1=CreateThread(NULL,0,ThreadProc,pLstOne,0,&dwThreadId1);
if(!handle1)
{
AfxMessageBox(_T("Thread 1 Create fail"));
}
CreateThread函数创建了一个新的线程,返回值为新线程的句柄。原型
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpsa,
DWORD cbStack,
LPTHREAD_START_ROUTINE lpStartAddr,
LPVOID lpvThreadParam,
DWORD fdwCreate,
LPDWORD lpIDThread
);
When ignored, the default stack size for a thread is determined by the linker setting /STACK.
//为自己所用堆栈分配的地址空间大小,不支持
Note It is invalid to set lpStartAddr to NULL. If this value is passed in the parameter, the function will return ERROR_INVALID_PARAMETER.
//控制线程创建的附加标志
The following table shows the values for this parameter.
| Value | Description |
|---|---|
| CREATE_SUSPENDED | The thread is created in a suspended state and does not run until the ResumeThread function is called. The thread can be run immediately after creation if the flag is not specified. |
| STACK_SIZE_PARAM_IS_A_RESERVATION | The cbStack parameter specified the maximum stack size instead of being ignored. This is a Windows CE only flag for the CreateThread function and is used to reserve the stack size for the thread created. By default, a thread is created with 64 KB stack reserved. You can use this flag to increase the stack size for the new thread that was created. |
If this parameter is NULL, the thread identifier is not returned.
线程函数必须写成这样的形式:
DWORD CThreadAsynDlg::ThreadProc(PVOID pArg)
{
CListBox * pListBox;
pListBox=(CListBox*)pArg; //这里对参数进行强制类型转换
}
线程函数应该尽可能使用函数参数和局部变量.
DWORD WINAPI FirstThread(PVOID pvParam) {
int x=0;
DWORD dwThreadID();
HANDLE hThread=createThread(NULL,0,SecondThread,(PVOID) &x,0, &dwThreadId);
closeHandle(hThread);
return(0);
}
DWORD WINAPI SecondThread(PVOID pvParam) {
//do some
*((int *) pvParam)=5;
.
.
return(0);
}
二、线程同步
方法1: 利用事件对象同步,具体在绿皮的evc高级编程那本书的p164。
简单地说是这样的步骤
利用CreateEvent创建一个事件对象,将参数bManualReset设为FALSE,参数bInitialState也设为FALSE,此时事件对象状态为“未标识”。然后在线程里通过调用WaitForSingleObject来等待事件被标识。此时,只要主线程调用SetEvent函数,将事件对象设置成已标识。那么线程里的WaitForSingleObject函数便会返回,继续执行,同时将事件对象状态设置成未标识。
1. 添加一个线程同步事件句柄 HANDLE m_hSynEvent;
2. 创建一个线程同步事件对象,一般在窗口的OnInitDialog事件中
m_hSynEvent = CreateEvent(NULL, false, true, NULL);
在窗口关闭时候,要关闭线程同步事件对象:
CloseHandle(m_hSynEvent);
3. 利用WaitForSingleObject来阻塞。具体看绿皮书165页。
if(WaitForSingleObject(pDlg->m_hSynEvent,INFINITE)==WAIT_OBJECT_0)
//上面的WaitForSingleObject实现阻塞,如果m_hSynEvent被标识了,那么才运行下面的代码。
{
for(int i=0;i
pDlg->m_incNum++;
pDlg->m_aGlobalData[i] = pDlg->m_incNum;
Sleep(5);
}
for(i=0;i
_itow(pDlg->m_aGlobalData[i],buffer,10);
pListBox->AddString(buffer);
}
}
标记事件对象
函数原型: BOOL SetEvent(HANDLE hEvent); //hEvent是事件对象的句柄
例如这样:SetEvent(pDlg->m_hSynEvent);
SetEvent是把事件对象设置成已标识状态。
BOOL ResetEvent(HANDLE hEvent);
此函数用于把事件对象设置成未标识状态。
Treo 700w正式发布,兼谈为何Palm会输给Windows Mobile(转)
Palm、Microsoft和Verizon在北京时间今天早上举行了联合新闻发布会,发布了新的Treo 700w手机,运营商是Verizon,操作系统是Windows Mobile 5.0。BillG代表Microsoft参加了新闻发布会,代表Palm的是Ed Colligan,不是Jeff Hawkins。
也就是短短两年多前的2003年6月,那时Palm收购了Handspring,Jeff Hawkins回到了他创建的Palm,人们纷纷将其与重回苹果的Steve Jobs相提并论。还记得Treo 600发布时候的轰动,在纽约的发布会上,Jeff Hawkins手里拿着Treo 600:

但自那以后,2004第一季度Pocket PC的出货量追平基于Palm OS的PDA,2004年底微软首次在手持设备市场上超越Palm占据最大份额,2005年初Sony宣布不再生产(基于Palm OS)的PDA,直到今天,Palm开始在自己的设备中使用微软的嵌入式操作系统,这在即便是一年前也是令人难以想象的。而曾几何时,Palm OS在九十年代末占据了超过50%的市场份额,拥有压倒性的优势。
微软已经可以把Palm从竞争对手名单中划掉了——正如以前Netscape、VisiCalc、WordPerfect一样——尽管这个名单不断的有新的加进来,例如Google、RIM。
Windows Mobile赢得对Palm的竞争是无可挑剔的,尽管Palm的用户始终嘲笑微软不懂的嵌入式系统,嘲笑微软用写PC操作系统的方式来写嵌入式的OS,嘲笑Pocket PC上的软件不如Palm多。没有人可以指责微软利用了垄断优势来排挤Palm:微软并没有在Windows XP里预装ActiveSync。尽管Palm的开发者和拥趸始终认为微软缺乏创新、缺乏技术、缺乏对市场的认识,但事实上,缺乏这些的是Palm——否则微软不可能赢得对Palm的竞争。终端用户市场里,用户是用脚投票的,掏不掏钱全在买者;开发者社区中,程序员总是喜欢给更多的用户写软件,也总是喜欢在容易开发移植的平台上写软件;销售渠道中,零售商总是喜欢代理更好卖的产品,电信运营商也总是希望co-branding受欢迎的设备。
在移动和嵌入式领域,微软是一个后起的公司。后起的公司要取代已经取得成功的公司,一定要等到他们犯错误。李彦宏说Netscape犯了1.5个错误,那Palm呢?
我觉得Palm有点过于相信来自于早期成功的经验。
九十年代末,手持设备还处于黑白显示、内存只有2MB或更少的状态。当时的Palm OS能够螺蛳壳里做道场,在有限的硬件上实现了丰富的功能。但很多Palm的开发者和支持者就此相信,手持设备就是应该是简单的,不应该把台式机上的事情拿到手持设备上来做。
Palmer认为微软在Pocket PC上采用和desktop windows类似的编程模型(消息机制、API、动态链接库、TCP/IP等)是荒谬的,他们嘲笑微软不懂handheld device。没错,在当时的硬件上,类似桌面系统的编程模型会造成很大的负担。但Palmer没有看到硬件的发展,事实上,时至今日,PDA的内存已经达到64MB或者128MB,CPU的时钟频率已经和Pentium II时代的台式机CPU一样块,与此同时,同样的编程模型带来的好处逐渐显现出来——VC和eVC,以及.NET和.NET CF——桌面的程序员可以很轻易的转移到Windows Mobile上。别说我事后诸葛亮,其实2003年的时候我在hi-pda和人为此争得面红耳赤说的就是同样的话。
对于程序员来说,开发Palm的应用程序相对困难——仍然需要用C语言来写。这和桌面上的快速开发潮流是不相称的。在桌面上,九十年代末就已经是Delphi和Visual Basic大行其道了,到了二十一世纪初,Java也进入桌面,然后就是.NET。程序员总是愿意用更方便的方法来写程序。喜欢炫耀自己“能够修改内核”的毕竟是少数。Palm曾经可以搭上Java的班车。Java的确也有了一个Palm上的VM(叫做Personal Java),我在我当时的Palm Vx上用过,速度出奇的慢。原因也很简单:CPU不行,内存太小。
Palmer过于相信过去的成功经验,他们坚信Palm的哲学是正确的:handheld device上的程序就应该是短小、简洁的。当时,在Palm OS 3.1上,一个程序如果超过100k就会被认为是丑陋的;而如果一个程序小于20kb,就会得到很好的评价。Palmer嘲笑Pocket PC上的程序的体积庞大,动辄500k或更大。但事实上,程序短小带来的好处被闪存的迅速降价完全抵消了。
Palmer在很长的一段时间里认为Pocket PC的彩色是没有必要的,gray scale的Palm足够了。但事实上,没有人能抵挡彩色的诱惑:无论是彩色电视机,还是彩屏手机。Palmer一度还对Palm的电池寿命非常自豪。没错,当时gray scale的Palm的电池寿命至少是在10小时以上的,而Pocket PC的电池通常只能维持两个小时(的确如此,我当时用iPAQ 3630的时候每天都需要充电)。但Palmer并没有很正确地意识到导致Pocket PC的电池寿命过短的主要原因之一是彩色和带背光的屏幕。事实上,当Palm的PDA也用上彩屏以后(例如我曾经用过Palm Tungsten T),电池寿命和Pocket PC相差无几,优势荡然无存。
Palm犯过的错误还包括错过了网络,错过了多媒体,错过了中国市场。受到硬件和编程模型的限制,Palm上始终开发不出类似Pocket PC上的“招商银行掌上银行”的应用。Palmer曾经固执的认为在handheld devices上是不需要上网的(当然,Palm长期的160*160 pixels的分辨率也是无法上网的原因之一)。曾经一度,对于Palm上的音乐播放软件来说,可以后台播放也会成为亮点——而对于Pocket PC来说,完全是与生俱来的。
Palm最终还是进入中国了,大概在2003年的样子,不算太晚,但没有把销售和市场做好,失去了最后的机会。Apple在中国也一直没有做好应该做的事情。人们喜欢iPod,但却不知道去哪里能买到。
C语言的精确延时
J2ME游戏引擎结构
游戏引擎的结构很多,不过基本上都是框架+主循环。J2ME游戏的引擎结构一般由两个class组成,一个是MIDlet,一个是Canvas(或Displayable)。MIDlet类是游戏的框架外壳,负责游戏的启动,停止和退出,并负责命令的监听和处理。Canvas类是屏幕绘图的平台,负责游戏主循环和处理某些按键事件,要么作为特定的低级监控代码事件要么作为抽象的游戏动作,三个主要函数void run(),void render(Graphics g),void keyPressed(int keyCode)。
1。游戏框架
import javax.microedition.midlet.*;
public class GameMIDlet extends MIDlet {
private GameCanvas cgame;
private Command exitCmd = new Command("Exit",Command.EXIT,2);
public GameMIDlet() {
cgame= new gameCanvas();
cgame.addCommand(exitCmd);
cgame.setCommandListener(this);
}
protected void startApp() throws MIDletStateChangeException {
Display.getDisplay(this).setCurrent(cgame);
try{
Thread mythread = new Thread(cgame);
mythread.start();
}
catch(Error e){
destroyApp(false);
notifyDestroyed();
}
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
Display.getDisplay(this).setCurrent((Displayable)null);
}
public void commandAction(Command c,Displayable d){
if (c == exitCmd) {
destroyApp(false);
notifyDestroyed();
}
//other commands
//...
}
2。游戏主循环
Canvas(或FullCanvas,继承自Canvas)类实现Runnable这个接口,然后在其构造函数中创建一个线程,启动其run()函数,而run函数里面就包含了游戏的主循环。
import java.lang.Runnable;
import java.lang.InterruptedException;
import java.lang.Thread;
import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;
public class gameCanvas
extends GameCanvas
implements Runnable {
private boolean mTrucking;
private long mFrameDelay; //帧延时,毫秒为单位
public gameCanvas() {
super(true);
mFrameDelay = 30;
init();
}
public void start() {
mTrucking = true;
//开始主线程
Thread t = new Thread(this);
t.start();
}
public void stop() { mTrucking = false; }
public void run() {
Graphics g = getGraphics();
while(mTrucking == true) {
render(g);
try { Thread.sleep(mFrameDelay); }
catch (InterrupedException ie) {}
}
}
public synchronized void keyPressed(int keyCode) {
switch(getGameAction(keyCode)){
case Canvas.LEFT: /* 左移 */
//...
}
}
private void render(Graphics g) {
//绘图。。。
flushGraphics();
}
}