Codename One调用Media.play()播放音效是否存在内存泄漏问题?
问题解答
1. 你的代码是否正确?
你的代码业务逻辑没有错误,单次、低频调用可以正常在模拟器和真机运行。你遇到的Windows内存占满问题不属于代码业务逻辑错误,而是未释放Media原生资源+Windows模拟器底层实现特性导致的:
- 你每次调用
playSound都会新建一个Media实例,默认没有主动释放资源,且原代码中加载音效的输入输出流也未主动关闭,存在小的资源泄漏点 - Codename One的Windows模拟器音频底层依赖系统原生音频接口,未被主动释放的Media会占用系统级的音频句柄和内存,这部分内存不归属Java进程管控,所以任务管理器里看不到NetBeans/Java进程的内存异常,只会表现为Windows整体内存占用持续上涨。
2. 该问题是否会在设备端出现?
正常使用场景下不会在Android/iOS设备端出现。
移动端的Codename One底层实现会在Media对象被Java GC回收时,自动释放对应的原生音频资源,只要不是极端高频连续创建Media实例,设备端不会出现内存泄漏问题。不过如果是高频播放音效的业务场景,依然建议主动做资源管理,避免出现音频占用、无法播放等偶发异常。
3. Windows模拟器环境的解决方法
有两种常用的解决方案,你可以根据业务场景选择:
方案1:缓存复用Media实例(推荐用于高频使用的短音效)
提前把常用的音效对应的Media实例加载缓存到内存里,每次播放直接调用缓存实例的play()方法,不需要每次新建实例,从根源避免资源泄漏。
// 类成员变量,缓存常用音效的Media实例 private Map<String, Media> soundCache = new HashMap<>(); private void playSound(String fileName) { try { // 优先从缓存取Media实例 if (soundCache.containsKey(fileName)) { Media cachedMedia = soundCache.get(fileName); // 重置播放位置到开头再播放 cachedMedia.setTime(0); cachedMedia.play(); return; } // 缓存不存在时走原来的加载逻辑 FileSystemStorage fss = FileSystemStorage.getInstance(); String sep = fss.getFileSystemSeparator() + ""; String soundDir; if (fss.getAppHomePath().endsWith(sep)) { soundDir = fss.getAppHomePath() + "sounds"; } else { soundDir = fss.getAppHomePath() + sep + "sounds"; } if (!fss.exists(soundDir)) { fss.mkdir(soundDir); } String filePath = soundDir + sep + fileName; if (!fss.exists(filePath)) { InputStream is = Display.getInstance().getResourceAsStream(getClass(), "/" + fileName); OutputStream os = fss.openOutputStream(filePath); com.codename1.io.Util.copy(is, os); // 主动关闭流释放资源 is.close(); os.close(); } Media media = MediaManager.createMedia(filePath, false); // 加载完成后存入缓存 soundCache.put(fileName, media); media.play(); } catch (IOException ex) { log("Error playing " + fileName + " " + ex.getMessage()); } }
方案2:播放完成后主动释放Media资源
如果不适合缓存(比如音效数量多、使用频率低),可以给Media添加播放完成回调,播完就调用cleanup()释放原生资源:
private void playSound(String fileName) { try { FileSystemStorage fss = FileSystemStorage.getInstance(); String sep = fss.getFileSystemSeparator() + ""; String soundDir; if (fss.getAppHomePath().endsWith(sep)) { soundDir = fss.getAppHomePath() + "sounds"; } else { soundDir = fss.getAppHomePath() + sep + "sounds"; } if (!fss.exists(soundDir)) { fss.mkdir(soundDir); } String filePath = soundDir + sep + fileName; if (!fss.exists(filePath)) { InputStream is = Display.getInstance().getResourceAsStream(getClass(), "/" + fileName); OutputStream os = fss.openOutputStream(filePath); com.codename1.io.Util.copy(is, os); is.close(); os.close(); } Media media = MediaManager.createMedia(filePath, false); // 添加播放完成回调,释放原生资源 media.addCompletionHandler(() -> { media.cleanup(); }); media.play(); } catch (IOException ex) { log("Error playing " + fileName + " " + ex.getMessage()); } }
你之前测试的其他音效加载代码出现相同问题,核心原因同样是未对Media实例做资源管理,按照上述两种方案修改后即可解决模拟器内存泄漏、部分音效丢失的问题。
内容的提问来源于stack exchange,提问作者J-J
相关产品推荐
相关产品推荐

