找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 11|回复: 0

java游戏开发规范_兼容真机(丢给AI开发)

[复制链接]

3万

主题

8

回帖

3万

积分

管理员

积分
38254
发表于 9 小时前 |中国| 显示全部楼层 |阅读模式
Java游戏开发规范_兼容真机

by 小扩
只读本文,即可从零写出能在 真功能机 / J2ME Loader 上运行的 J2ME(MIDP)程序,并完成编译打包。可下载附件丢给ai或者复制粘贴全文

API 是否存在:一律查 J2ME Docs不要用桌面 JDK 文档




一、先搞清目标

环境作用
FreeJ2ME / KEmulator桌面调试方便,但常多实现了真机没有的 API,不能当验收标准
J2ME Loader(Android)日常验收;比真机宽松,但比桌面模拟器接近
真功能机最终标准;分辨率、按键、声音、文件路径因机而异


默认目标配置:CLDC-1.1 + MIDP-2.0。更老的机再降到 CLDC-1.0 / MIDP-1.0,并同步改 Manifest(见第七节)。

做不到「一份 JAR 通杀所有机」:本文解决的是「能编译、能启动、混淆不崩、API 不越界」;屏幕/按键/媒体仍要按机型适配。

备注:媒体操作的项目,比游戏类项目更难兼容

市面小游戏常写「全机型」,多半是:只测过主流 MIDP-2.0 机、主流程能玩就算成功,并非从 MIDP-1.0 黑白机到带 JSR-75 的机一份 JAR 功能全开。大厂当年也是多包、砍功能或只保核心玩法。

他们显得稳,是因为用了最低公因数玩法
  • 只用 Canvas + getGameAction + 少量 RMS,不碰文件 / HTTP / MMAPI
  • JAR 往往几十 KB,老机装得下
  • 布局简单,按 getWidth/getHeight 缩放或只适配一两档分辨率
  • 不依赖签名与敏感权限
  • 媒体失败就静音跳过,主循环不崩


媒体操作模拟 产品形态不同,兼容面天然更窄:

用到的能力机型差异
JSR-75 文件很多机没有;有也要权限 / 签名
MMAPI 音乐格式、并发、路径因机而异
JAR 体积偏大(常数百 KB)不少老机上限更低
时钟等逻辑用 double纯 CLDC-1.0 机直接不行
浏览器 / 文件等完整功能安全域一卡就挂


一句话:别人通杀的是「最低公因数玩法」;媒体操作要的是「多模块真机能力」。要接近前者的兼容面,只能:核心壳只靠 MIDP 标准 API,文件 / 音乐做成可选降级,再压体积——主界面在无 JSR 机上仍能进,高级功能有就开、没有就提示。

本文还覆盖四块真机落地内容:

主题章节解决什么
机型适配模板软键/布局/媒体可抄代码,不只写原则
降级清单老机 CLDC-1.0 / MIDP-1.0 要砍哪些 API
签名与权限声明了权限却仍 SecurityException
安装与排障JAD+JAR 怎么装上;真机无日志怎么查





二、工程长什么样


  1. MyMidlet/
  2.   src/
  3.     HelloMidlet.java           # 入口,extends MIDlet
  4.     ...
  5.   res/
  6.     META-INF/MANIFEST.MF
  7.     icon.png                   # 边长建议 ≤ 48
  8.   java_lib/
  9.     jsr139-cldcapi11.jar       # CLDC 1.1 完整 API(不要用自写 stub)
  10.     jsr118-midpapi20.jar       # MIDP 2.0
  11.     jsr075-pda.jar             # 仅当用文件 API
  12.     jsr135-mmapi.jar           # 仅当用 Player
  13.     compiler/
  14.       ecj-4.20.jar
  15.       proguard.jar
  16.       default.pro
  17.   build/                       # 编译输出 .class
  18.   release/
  19.     unverified.jar             # 未混淆,先测这个
  20.     MyApp_v1.0.0.jar           # 混淆后给 Loader/真机
  21.     MyApp_v1.0.0.jad           # 真机安装常用
复制代码


三条硬规则:
  • 编译只用 完整 JSR JAR 当 bootclasspath,禁止自写 stub(stub 缺方法时桌面能过、真机才炸)。
  • 不要把 javax.microedition.* 的 class 打进自己的 JAR(机子自带实现)。
  • 资源路径大小写与 JAR 内一致;代码里用 "/icon.png" 这种以 / 开头的路径。


JSR JAR 来源:Sun/Oracle Java ME SDK、WTK 2.5.2 的 lib/,或任何带完整 CLDC/MIDP 签名的工具链包。文件名不重要,内容必须是真 API。




三、怎么写代码(核心)

3.1 最小可运行 MIDlet


  1. import javax.microedition.lcdui.Display;
  2. import javax.microedition.lcdui.Form;
  3. import javax.microedition.midlet.MIDlet;

  4. public class HelloMidlet extends MIDlet {
  5.     protected void startApp() {
  6.         Form form = new Form("Hello");
  7.         form.append("OK");
  8.         Display.getDisplay(this).setCurrent(form);
  9.     }

  10.     protected void pauseApp() {
  11.         // 停定时器、停 Player、可释放大图
  12.     }

  13.     protected void destroyApp(boolean unconditional) {
  14.         // 关闭文件 / RMS / Player
  15.     }
  16. }
复制代码


Manifest 里 MIDlet-1 的第三段必须是这个类的全名(无包则 HelloMidlet,有包则 com.foo.HelloMidlet)。

3.2 语言与标准库:只能用缩水版

按 Java 1.3 写。下面这些在 CLDC 里 没有或不完整

禁止正确写法
ArrayList / HashMap / HashSetVector / Hashtable / 数组
StringBuilderStringBuffer
泛型、enum、增强 for、... 变长参数、注解普通 Java 1.3
Math.round(x)见下方
String.format / printfStringBuffer 拼接
java.util.concurrent.*Thread + synchronized + wait/notify
依赖 System.out 当日志真机无控制台;用 Loader 日志或自己写文件/RMS
反射乱挖私有 API真机常禁或不全;不要依赖


四舍五入(CLDC 有 Math.floor,无 round):

  1. private static int round(double v) {
  2.     return (int) Math.floor(v + 0.5);
  3. }
复制代码


负数边界若要与 J2SE Math.round 完全一致,自行加单测;一般 UI 坐标用上面即可。

每用一个 java.lang / java.util / java.io 方法,都到 J2ME Docs 确认存在。

3.3 LCDUI 必记

TextBox 约束常量在 TextField 上(写 TextBox.ANY 会在真 API 下编不过,或在 stub 下误通过):

  1. import javax.microedition.lcdui.TextBox;
  2. import javax.microedition.lcdui.TextField;

  3. // 正确
  4. TextBox box = new TextBox("标题", "", 64, TextField.ANY);
  5. // TextField.NUMERIC / PASSWORD / URL / PHONENUMBER ...
复制代码


Canvas.paint 必须是 protected

  1. import javax.microedition.lcdui.Canvas;
  2. import javax.microedition.lcdui.Graphics;

  3. public class GameCanvas extends Canvas {
  4.     protected void paint(Graphics g) {   // 必须 protected
  5.         g.setColor(0xFFFFFF);
  6.         g.fillRect(0, 0, getWidth(), getHeight());
  7.     }
  8. }
复制代码


布局用 getWidth() / getHeight(),不要写死 240×320。全屏:setFullScreenMode(true)(MIDP 2)。

按键:优先 getGameAction(keyCode)(UP/DOWN/LEFT/RIGHT/FIRE)。左右软键键码 各厂商不同,不要只认诺基亚的 -6/-7,做成可配置映射(见第六节模板)。

多数功能机 无触屏:主流程必须能全靠按键完成;pointerPressed 只能当附加。

字体只有 Font.SIZE_SMALL / MEDIUM / LARGE 与 FACE_* / STYLE_*,不要假设像素字号。

3.4 多界面时:不要让 Canvas 实现带 paint 的业务接口

接口里的方法在字节码中是 public。若业务接口也叫 paint(Graphics),而 Canvas 子类去 implements 它,就会和 MIDP 要求的 protected paint 冲突——未混淆可能碰巧能跑,ProGuard 合并接口/类之后 Loader/真机直接 IllegalAccessError

正确拆分:

  1. import javax.microedition.lcdui.Graphics;

  2. /** 业务界面:paint 必须是 public */
  3. public interface AppScreen {
  4.     void paint(Graphics g);
  5.     void keyPressed(int keyCode);
  6. }

  7. /** 仅按键回调,不要声明 paint */
  8. public interface KeyHandler {
  9.     void onKey(int keyCode);
  10. }
复制代码

  1. import javax.microedition.lcdui.Canvas;
  2. import javax.microedition.lcdui.Graphics;

  3. public class AppCanvas extends Canvas implements KeyHandler {
  4.     private AppScreen screen;

  5.     public void setScreen(AppScreen s) { screen = s; }

  6.     protected void paint(Graphics g) {          // Canvas 要求:protected
  7.         if (screen != null) screen.paint(g);
  8.     }

  9.     public void onKey(int keyCode) { }
  10.     protected void keyPressed(int keyCode) {
  11.         if (screen != null) screen.keyPressed(keyCode);
  12.         repaint();
  13.     }
  14. }
复制代码

  1. import javax.microedition.lcdui.Graphics;

  2. public class MenuScreen implements AppScreen {
  3.     public void paint(Graphics g) {             // 接口要求:public
  4.         g.drawString("Menu", 0, 0, Graphics.TOP | Graphics.LEFT);
  5.     }
  6.     public void keyPressed(int keyCode) { }
  7. }
复制代码


规则一句话:Canvas 子类只负责转发;带 paint 的接口只由 Screen 类实现。

3.5 生命周期与线程

  • startApp / pauseApp / destroyApp 要能反复进出。
  • pauseApp:停 Timer、停 Player、可丢掉大图。
  • destroyApp:关 RMS、文件、Player(deallocate + close)。
  • 不要在 paint 里读文件、解码大图、发网络请求。
  • 自建线程里不要直接改 LCDUI / 碰 Graphics。后台算完后,用 Display.callSerially 回到事件线程再改界面:


  1. import javax.microedition.lcdui.Display;

  2. // 在工作线程末尾:
  3. display.callSerially(new Runnable() {
  4.     public void run() {
  5.         // 这里才改 Form / 切 Screen / 调 repaint()
  6.         canvas.repaint();
  7.     }
  8. });
复制代码

Timer / TimerTask 回调同样当作后台线程:需要改 UI 时走 callSerially。

3.6 RMS / 文件 / 网络 / 声音

RMS(小配置):

  1. import javax.microedition.rms.RecordStore;

  2. RecordStore rs = RecordStore.openRecordStore("cfg", true);
  3. // ... read/write ...
  4. rs.closeRecordStore();
复制代码

空间很小(常只有几十~几百 KB),不要当大文件盘。注意 RecordStoreFullException。

文件(JSR-75):Manifest 声明权限;路径用 FileSystemRegistry.listRoots(),不要写死 C:。无此 JSR 的机不要编译进文件代码,或做成可选。

HTTP:需权限与用户授权;HTTPS/证书行为分裂,不要假设桌面那套。超时与断网要 catch,不要卡死事件线程。

MMAPI 声音:格式看机型(Tone / MIDI / WAV / AMR / MP3)。用完:

  1. player.stop();
  2. player.deallocate();
  3. player.close();
  4. player = null;
复制代码

同时尽量只保持一个 Player。大图:缩小后再 Image.createImage,用完丢掉引用,防 OutOfMemoryError。

3.7 中文、编码与资源

  • JAR 内路径大小写敏感(Windows 开发时最容易踩坑)。
  • 不少功能机中文字体缺字 → 方框;重要 UI 可用图片字,或接受系统字体限制。
  • 文本文件编码:优先 UTF-8 并自己解析;部分机 new String(bytes, "UTF-8") 会抛异常,需 catch 后回退 ISO-8859-1 或机型已知编码。
  • 图片:PNG 最稳;JPEG/透明度因机而异。图标建议不透明小 PNG,边长 ≤ 48。
  • 资源用 Image.createImage("/foo.png") 或 getClass().getResourceAsStream("/foo.png"),路径以 / 开头且与 JAR 内一致。


3.8 禁止依赖厂商私有 API

桌面模拟器常带 Nokia UI、Samsung、Siemens 等扩展包,真机未必有。默认只允许:
  • CLDC + MIDP(javax.microedition.* 标准子集)
  • 明确目标机支持的可选 JSR(如 JSR-75、JSR-135)

不要 import com.nokia.* / com.samsung.* 等,除非你在为单一厂商机型出专用包,并在验收机上实测。




四、Manifest 与 JAD

4.1 res/META-INF/MANIFEST.MF

  1. Manifest-Version: 1.0
  2. MIDlet-Name: 应用名
  3. MIDlet-Version: 1.0.0
  4. MIDlet-Vendor: 厂商
  5. MIDlet-Icon: /icon.png
  6. MIDlet-1: 应用名, /icon.png, HelloMidlet
  7. MicroEdition-Profile: MIDP-2.0
  8. MicroEdition-Configuration: CLDC-1.1
复制代码


用了文件读写时追加(行宽超过 72 字符必须折行,续行以一个空格开头):

  1. MIDlet-Permissions: javax.microedition.io.Connector.file.read,javax.micr
  2. oedition.io.Connector.file.write
复制代码


其它权限按需(乱写可能导致装不上):
  • javax.microedition.io.Connector.http
  • javax.microedition.io.Connector.https
  • javax.microedition.io.Connector.socket


检查:MIDlet-1 第三段 = 真实入口类;图标路径在 JAR 里存在。

权限与签名的关系见第八节:声明了权限 ≠ 未签名包一定能用。

4.2 MyApp_v1.0.0.jad(真机常用)

与 Manifest 关键字段保持一致,并增加:

  1. MIDlet-Jar-URL: MyApp_v1.0.0.jar
  2. MIDlet-Jar-Size: 123456
复制代码

MIDlet-Jar-Size = 最终 JAR 的字节数(混淆后要重算)。JAD 与 JAR 放同一目录。




五、怎么编译打包

流水线:

  1. 源码 → ECJ/javac(1.3, bootclasspath=真JSR) → jar cfm → unverified.jar
  2.      → ProGuard -microedition → 最终.jar → 写 JAD
  3.      → 先测 unverified,再测最终包 → 真机再测
复制代码


5.1 编译(ECJ 示例)

假设在项目根目录,Linux/macOS 用 :,Windows 的 java.exe 用 ;:

  1. # bootclasspath / classpath 都指向这些 JSR,不要用桌面 rt.jar

  2. java -cp java_lib/compiler/ecj-4.20.jar ^
  3.   org.eclipse.jdt.internal.compiler.batch.Main ^
  4.   -1.3 -encoding UTF-8 -proc:none -g:none ^
  5.   -bootclasspath "java_lib/jsr139-cldcapi11.jar;java_lib/jsr118-midpapi20.jar" ^
  6.   -classpath "java_lib/jsr139-cldcapi11.jar;java_lib/jsr118-midpapi20.jar" ^
  7.   -d build src
复制代码


也可用 javac -source 1.3 -target 1.1,同样必须设 bootclasspath 为 JSR。

5.2 打包未混淆 JAR

  1. jar cfm release/unverified.jar res/META-INF/MANIFEST.MF -C res . -C build .
复制代码


自检:jar tf release/unverified.jar 应看到 META-INF/MANIFEST.MF、入口 .class、icon.png;不应出现整份 JDK 的 java/lang/String.class 或把 JSR 实现打进去。

5.3 ProGuard(完整可用配置)

保存为 java_lib/compiler/default.pro:

  1. -verbose
  2. -dontnote
  3. -microedition
  4. -overloadaggressively
  5. -repackageclasses ''

  6. # 禁止打开下面两行(Canvas + 自定义 paint 接口时会 IllegalAccessError):
  7. # -mergeinterfacesaggressively
  8. # -allowaccessmodification

  9. -keep class * extends javax.microedition.lcdui.Canvas
  10. -keep public class * extends javax.microedition.midlet.MIDlet

  11. -keepclasseswithmembernames class * {
  12.     native <methods>;
  13. }
复制代码


运行(-libraryjars 指向 JSR,不要 injars):

  1. java -cp java_lib/compiler/proguard.jar proguard.ProGuard ^
  2.   -injars release/unverified.jar ^
  3.   -outjars release/MyApp_v1.0.0.jar ^
  4.   -libraryjars java_lib/jsr139-cldcapi11.jar ^
  5.   -libraryjars java_lib/jsr118-midpapi20.jar ^
  6.   -include java_lib/compiler/default.pro
复制代码


混淆后建议 javap 抽查:
  • 入口类仍 extends MIDlet
  • Canvas 子类的 paint 仍是 protected
  • 实现 AppScreen 的类 paint 为 public
  • Canvas 没有 implements 带 paint 的业务接口


5.4 Preverify(真机)

部分老机要求 class 经过 WTK / Java ME SDK 的 preverify。ProGuard 的 -microedition 会做 ME 相关处理;若真机仍报 verify / 类校验错误,用官方工具处理后再打包:

  1. # 示例:WTK / Java ME SDK 自带 preverify(路径按本机安装改)
  2. preverify -classpath "java_lib/jsr139-cldcapi11.jar;java_lib/jsr118-midpapi20.jar" ^
  3.   -d build_preverified build
  4. jar cfm release/MyApp_v1.0.0.jar res/META-INF/MANIFEST.MF ^
  5.   -C res . -C build_preverified .
复制代码

Loader 能跑 ≠ 真机能装。

5.5 一份可直接改的 make.sh(含 Windows 与 JAD)

  1. #!/usr/bin/env bash
  2. set -e
  3. ROOT="$(cd "$(dirname "$0")" && pwd)"
  4. cd "$ROOT"
  5. rm -rf build release
  6. mkdir -p build release

  7. APP_NAME="MyApp_v1.0.0"

  8. # Windows Git Bash / MSYS 下原生 java.exe 需要 ';'
  9. case "$(uname -s 2>/dev/null)" in
  10.   MINGW*|MSYS*|CYGWIN*) CP_SEP=';' ;;
  11.   *) CP_SEP=':' ;;
  12. esac

  13. LIBS="$(find java_lib -maxdepth 1 -name '*.jar' | sort | tr '\n' "$CP_SEP" | sed "s/${CP_SEP}\$//")"

  14. java -cp java_lib/compiler/ecj-4.20.jar \
  15.   org.eclipse.jdt.internal.compiler.batch.Main \
  16.   -1.3 -encoding UTF-8 -proc:none -g:none \
  17.   -bootclasspath "$LIBS" -classpath "$LIBS" \
  18.   -d build src

  19. jar cfm release/unverified.jar res/META-INF/MANIFEST.MF -C res . -C build .

  20. PG_ARGS=""
  21. for j in java_lib/*.jar; do
  22.   PG_ARGS="$PG_ARGS -libraryjars $j"
  23. done

  24. java -cp java_lib/compiler/proguard.jar proguard.ProGuard \
  25.   -injars release/unverified.jar \
  26.   -outjars "release/${APP_NAME}.jar" \
  27.   $PG_ARGS \
  28.   -include java_lib/compiler/default.pro

  29. # 生成 JAD(字段与 Manifest 保持一致,按项目改名称)
  30. SIZE=$(wc -c < "release/${APP_NAME}.jar" | tr -d ' ')
  31. cat > "release/${APP_NAME}.jad" <<EOF
  32. MIDlet-Name: 应用名
  33. MIDlet-Version: 1.0.0
  34. MIDlet-Vendor: 厂商
  35. MIDlet-Jar-URL: ${APP_NAME}.jar
  36. MIDlet-Jar-Size: ${SIZE}
  37. MIDlet-Icon: /icon.png
  38. MIDlet-1: 应用名, /icon.png, HelloMidlet
  39. MicroEdition-Profile: MIDP-2.0
  40. MicroEdition-Configuration: CLDC-1.1
  41. EOF

  42. echo "OK: release/unverified.jar / release/${APP_NAME}.jar / release/${APP_NAME}.jad"
复制代码


更完整的 Windows 路径转换(cygpath、参数文件)可参考本仓库 project/make.sh。

体积:很多老机对 JAR 有硬上限(常见约 64KB~数百 KB,因机而异)。资源过大时先砍图、砍音、加强混淆,再谈功能。




六、机型差异与可抄模板

原则不够用时,直接抄下面骨架,再按实测改常量。

6.1 原则表

维度写法要求
屏幕只用 getWidth/getHeight 布局;按宽度档位换布局(如 <176 / <240 / ≥240)
软键键码可配置,不写死单一厂商
内存少同时持有大图;及时释放 Player/Image
文件listRoots();无 JSR-75 则降级
声音格式失败要 catch,不要假定 MP3
触屏按键可走完主流程
权限SecurityException 时提示或关闭功能
编码/字体UTF-8 失败回退;关键文案可图片字


6.2 软键映射模板

  1. public final class SoftKeys {
  2.     // 可按机型改这两个值;诺基亚常见 -6/-7,其它厂可能不同
  3.     public static int LEFT = -6;
  4.     public static int RIGHT = -7;

  5.     public static boolean isLeft(int keyCode) {
  6.         return keyCode == LEFT;
  7.     }

  8.     public static boolean isRight(int keyCode) {
  9.         return keyCode == RIGHT;
  10.     }

  11.     /** 调试屏:把实际按下的 keyCode 显示出来,写进配置 */
  12.     public static void learn(int keyCode) {
  13.         // 显示 "key=" + keyCode,让用户指定左/右软键
  14.     }
  15. }
复制代码


全屏 Canvas 时左右软键常靠 keyPressed;非全屏也可用 Command + CommandListener(更标准,但样式因机而异)。

6.3 屏幕档位布局模板

  1. int w = canvas.getWidth();
  2. int h = canvas.getHeight();
  3. int pad = (w < 176) ? 4 : (w < 240) ? 6 : 8;
  4. int line = (w < 176) ? 14 : 16;
  5. // 列表行高、图标尺寸都跟 w/h 走,禁止写死 240x320
复制代码


6.4 媒体/文件失败降级

  1. try {
  2.     player = Manager.createPlayer(is, "audio/midi");
  3.     player.start();
  4. } catch (Exception e) {
  5.     // 静音继续,不要让整个 MIDlet 崩
  6.     player = null;
  7. }

  8. try {
  9.     roots = FileSystemRegistry.listRoots();
  10. } catch (Throwable t) {
  11.     roots = null; // UI 提示「本机无文件访问」
  12. }
复制代码





七、降到 CLDC-1.0 / MIDP-1.0(降级清单)

目标机若只有更老 Profile,除改 Manifest 外,代码也要跟着砍。

7.1 Manifest

  1. MicroEdition-Profile: MIDP-1.0
  2. MicroEdition-Configuration: CLDC-1.0
复制代码


编译用对应的 CLDC 1.0 / MIDP 1.0 JSR JAR,不要继续链 CLDC 1.1 API 却声称 1.0。

7.2 CLDC-1.0 常见砍掉的能力

能力说明
float / double没有浮点类型与 Math 浮点方法;改定点数(整数放大 100/1000 倍)
部分库方法仍以 J2ME Docs / 1.0 JAR 为准,不要假设 1.1 有的都在


7.3 MIDP-1.0 常见砍掉的能力

能力说明
Canvas.setFullScreenMode没有;接受系统标题栏/软键栏占位
GameCanvas / Layer / Sprite 等 game 包MIDP 2 才有;自己用 Canvas 画
部分 LCDUI / 媒体细节以 1.0 文档为准


策略:能两套包就出 xxx_midp2.jar 与 xxx_midp1.jar;或主路径只用 1.0 子集(老机反射也弱,优先分包)。




八、权限、签名与安全域

真机通常把 MIDlet 放在不同安全域(未信任 / 信任 / 厂商等)。

情况常见结果
未声明 MIDlet-Permissions文件/网络等敏感 API 直接不可用或装不上
声明了权限,但包未签名能装、能启动,一用文件/网络就 SecurityException 或反复弹授权(因机而异)
用厂商/运营商认可的证书签名才较可能静默获得文件、HTTP 等权限


实务建议:
  • Manifest 只声明真正用到的权限。
  • 所有 Connector.open / FileConnection / HTTP 调用包 try/catch (SecurityException e),失败时给用户可读提示并关闭该功能。
  • 若目标真机要求签名:用 Java ME / WTK 的签名工具对 JAR 签名,并按厂商文档导入证书;JAD 与 JAR 的签名相关字段保持一致
  • Loader 上「一切畅通」不能证明未签名包在真机有文件权限。


开发阶段可用「无文件/无网络」功能集先过启动验收,再单独做权限机型矩阵。




九、真机安装与无日志排障

9.1 常见安装途径

方式说明
蓝牙 / USB 传 JAD+JAR多数机要求二者同目录、Size 正确
存储卡拷贝部分机从文件管理器「打开」JAR/JAD
OTA(HTTP 下 JAD)MIDlet-Jar-URL 必须可下载;服务器 MIME 常要配对


装不上时优先查:JAD Size、入口类名、图标是否存在、Profile 是否高于手机、JAR 是否超体积上限。

9.2 真机几乎没有控制台时

  • 先在 J2ME Loader 看 JL-Mod/log.txt(路径视加载器而定)。
  • 真机:自写「调试屏」显示最后错误字符串;或写入 RMS /(有权限时)小日志文件。
  • 二分法:unverified → 混淆包;无文件版 → 有文件版;缩小资源后再装。
  • 安装失败但无文案:换 JAD、核对 Size、试只装更小的 hello MIDlet 验证机子 Java 环境。





十、验收顺序



    [] 真 JSR 编译通过(无 stub、无厂商私有 API)。
    [] unverified.jar 在 J2ME Loader 能进主界面。
    [] 混淆后的 JAR 也能进(必测;paint/接口问题只在这一步暴露)。
    [] pauseApp / 退出不崩;无触屏仅按键可玩;后台线程改 UI 走 callSerially。
    [] 目标真机:JAD+JAR 能装、能启动;核对 MIDlet-Jar-Size。
    [] 若用文件/网络:在真机验证权限(含签名策略),SecurityException 有降级提示。
  • 老机:按第七节降 Profile 的包单独测。





十一、排错

现象处理
编译缺 Math.round 等换 CLDC 写法;查 J2ME Docs
ANY 找不到TextField.ANY,不要 TextBox.ANY
安装后找不到 MIDlet查 MIDlet-1 类名、图标、JAD Size
未混淆能跑、混淆崩 IllegalAccessError + paint关掉 mergeinterfaces / allowaccessmodification;keep Canvas;按 3.4 拆接口
NoSuchMethodError / NoClassDefFoundError用了机子没有的 JSR、更高 Profile,或厂商私有 API
OutOfMemoryError缩图、释放 Player/Image、减小 JAR
SecurityException权限未声明 / 未签名 / 用户拒绝;catch 并降级
类校验 / verify 失败走 5.4 preverify;确认 -microedition
仅 FreeJ2ME 能跑模拟器多实现了 API;用真 JSR 重编,改在 Loader/真机测
中文方框 / 乱码字体缺字用图;编码回退;勿依赖桌面默认 UTF-8
软键无反应用调试屏记录 keyCode,改 SoftKeys 映射


Loader 日志常见路径:设备上的 JL-Mod/log.txt(视具体加载器而定)。




十二、从零检查清单(可放到项目里打勾)

写代码

    [] 语法 ≤ Java 1.3;集合用 Vector/Hashtable
    [] 无 Math.round / StringBuilder / ArrayList / HashMap
    [] 无厂商私有 API(除非单机专用包)
    [] TextBox 约束来自 TextField
    [] Canvas.paint 为 protected;业务 Screen 的 paint 为 public;Canvas 不实现业务 paint 接口
    [] 布局与按键不写死单一机型;主流程可纯按键;软键可配置
    [] 后台改 UI 使用 Display.callSerially
    [] pause/destroy 释放 Timer、Player、大图、文件
  • 文件/网络/播放失败有 catch 与降级


工程与包

    [] bootclasspath = 真 JSR,无 stub
    [] Manifest / 入口类 / 图标正确;超长行已折行
    [] JAR 未打入 javax.microedition 实现
    [] 先测 unverified,再测 ProGuard 包
    [] ProGuard 含 -microedition、keep MIDlet、keep Canvas;未开 mergeinterfacesaggressively / allowaccessmodification
    [] 真机需要时有 JAD,Size 正确;体积不超过目标机上限
    [] 需要时做过 preverify
    [] 敏感 API 的签名/权限策略已按目标机验证


老机 / 真机

    [] 若目标为 CLDC-1.0:无浮点
    [] 若目标为 MIDP-1.0:无 setFullScreenMode / Game API 等
  • 真机安装途径已试通;无日志时有调试屏或 RMS 日志


API 依据
  • 不熟悉的 API, 可在 J2ME Docs 查询





(文档结束)

java游戏开发规范_兼容真机.md

24.83 KB

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|幻蓝小站 |网站地图

GMT+8, 2026-8-12 18:37

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表