在桌面和移动端使用GraalVM加载和播放音频



我正在尝试加载和播放AudioClip在2个场景:

  1. 在开发过程中的桌面(从IDE),以便我可以在开发过程中测试事情是否正常工作。
  2. 移动端,使用Gluon的GraalVM原生解决方案。

媒体模块在Android上不支持,使用Attach的音频服务。

我所尝试的:检查AudioService是否存在;如果是,请加载声音并与服务一起播放;如果没有,请加载声音并使用Media播放。

  • 这个方法正确吗?我得到一个异常,如下所示。
  • 我(我可以)排除媒体模块依赖时,我部署到移动设备,因为它是不可用的?

示例类:

package com.audioTest;
import static com.gluonhq.charm.glisten.application.AppManager.*;
import javafx.application.Application;
import javafx.scene.control.Button;
import javafx.stage.Stage;
import com.gluonhq.charm.glisten.application.AppManager;
import com.gluonhq.charm.glisten.mvc.View;
public class MyApplication extends Application {
AppManager appManager = AppManager.initialize();
@Override
public void init() {
class HomeView extends View {

HomeView() {
var button = new Button("Play Sound");
button.setOnAction(e -> Notifier.create());
setCenter(button);
}
};
appManager.addViewFactory(HOME_VIEW, HomeView::new);
}
@Override
public void start(Stage stage) throws Exception {
appManager.start(stage);
if (com.gluonhq.attach.util.Platform.isDesktop()) {
stage.setHeight(600);
stage.setWidth(360);
stage.centerOnScreen();
}
}
public static void main(String args[]) {
launch(args);
}
}

package com.audioTest;
import javafx.scene.media.AudioClip;
import com.gluonhq.attach.audio.Audio;
import com.gluonhq.attach.audio.AudioService;
public abstract sealed class Notifier {
public static Notifier create() {
return AudioService.create().<Notifier>map(MobileNotifier::new).orElseGet(DesktopNotifier::new);
}
private Notifier() {}
private final static class MobileNotifier extends Notifier {
private final Audio BEEP;
private MobileNotifier(AudioService service) {
BEEP = service.loadSound(getClass().getResource("/sounds/Beep.wav")).orElseThrow(); // throws here
BEEP.play();
}
}
private final static class DesktopNotifier extends Notifier {
private static final AudioClip BEEP = new AudioClip(Notifier.class.getResource("/sounds/Beep.wav").toExternalForm());
private DesktopNotifier() {
BEEP.play();
}
}
}

这个pom是Gluon插件添加的音频服务和资源列表给出的标准pom:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.audioTest</groupId>
<artifactId>AudioTest</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>AudioTest</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven-compiler-plugin-version>3.10.1</maven-compiler-plugin-version>
<javafx-maven-plugin-version>0.0.8</javafx-maven-plugin-version>
<gluonfx-maven-plugin-version>1.0.14</gluonfx-maven-plugin-version>
<java-version>17</java-version>
<javafx-version>18.0.1</javafx-version>
<charm-version>6.1.0</charm-version>
<attach-version>4.0.14</attach-version>

<main.class>com.audioTest.MyApplication</main.class>
<app.identifier>${main.class}</app.identifier>
<app.description>The AudioTest app</app.description>
<package.type />
<mac.app.store />
<mac.signing.user.name />
<bundle.version />
<bundle.short.version />
<version.code />
<provided.keystore.path />
<provided.keystore.password />
<provided.key.alias />
<provided.key.alias.password />
</properties>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafx-version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-media</artifactId>
<version>${javafx-version}</version>
</dependency>
<dependency>
<groupId>com.gluonhq</groupId>
<artifactId>charm-glisten</artifactId>
<version>${charm-version}</version>
</dependency>
<dependency>
<groupId>com.gluonhq.attach</groupId>
<artifactId>display</artifactId>
<version>${attach-version}</version>
</dependency>
<dependency>
<groupId>com.gluonhq.attach</groupId>
<artifactId>lifecycle</artifactId>
<version>${attach-version}</version>
</dependency>
<dependency>
<groupId>com.gluonhq.attach</groupId>
<artifactId>statusbar</artifactId>
<version>${attach-version}</version>
</dependency>
<dependency>
<groupId>com.gluonhq.attach</groupId>
<artifactId>storage</artifactId>
<version>${attach-version}</version>
</dependency>
<dependency>
<groupId>com.gluonhq.attach</groupId>
<artifactId>audio</artifactId>
<version>${attach-version}</version>
</dependency>
<dependency>
<groupId>com.gluonhq.attach</groupId>
<artifactId>util</artifactId>
<version>${attach-version}</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>Gluon</id>
<url>https://nexus.gluonhq.com/nexus/content/repositories/releases</url>
</repository>
<repository>
<id>snapshot</id>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>snapshot</id>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</pluginRepository>
</pluginRepositories>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin-version}</version>
<configuration>
<release>${java-version}</release>
</configuration>
</plugin>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>${javafx-maven-plugin-version}</version>
<configuration>
<mainClass>${main.class}</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>com.gluonhq</groupId>
<artifactId>gluonfx-maven-plugin</artifactId>
<version>${gluonfx-maven-plugin-version}</version>
<configuration>
<verbose>true</verbose>
<target>${gluonfx.target}</target>
<attachList>
<list>display</list>
<list>lifecycle</list>
<list>statusbar</list>
<list>storage</list>
<list>audio</list>
</attachList>
<resourcesList>
<item>Beep.wav</item>
</resourcesList>
<mainClass>${main.class}</mainClass>
<appIdentifier>${app.identifier}</appIdentifier>
<releaseConfiguration>
<vendor>Gluon</vendor>
<description>${app.description}</description>
<packageType>${package.type}</packageType>
<!-- for macOS/iOS -->
<macAppStore>${mac.app.store}</macAppStore>
<bundleShortVersion>${bundle.short.version}</bundleShortVersion>
<bundleVersion>${bundle.version}</bundleVersion>
<!-- for Android -->
<versionCode>${version.code}</versionCode>
<providedKeyStorePath>${provided.keystore.path}</providedKeyStorePath>
<providedKeyStorePassword>${provided.keystore.password}</providedKeyStorePassword>
<providedKeyAlias>${provided.key.alias}</providedKeyAlias>
<providedKeyAliasPassword>${provided.key.alias.password}</providedKeyAliasPassword>
</releaseConfiguration>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>desktop</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<gluonfx.target>host</gluonfx.target>
</properties>
</profile>
<profile>
<id>mac</id>
<properties>
<package.type>pkg</package.type>
<mac.app.store>false</mac.app.store>
<bundle.version>${env.GITHUB_RUN_NUMBER}</bundle.version>
<bundle.short.version>1.0</bundle.short.version>
</properties>
</profile>
<profile>
<id>macstore</id>
<properties>
<package.type>pkg</package.type>
<mac.app.store>true</mac.app.store>
<bundle.version>1.${env.GITHUB_RUN_NUMBER}</bundle.version>
<bundle.short.version>1.6</bundle.short.version>
</properties>
</profile>
<profile>
<id>ios</id>
<properties>
<gluonfx.target>ios</gluonfx.target>
<bundle.version>${env.GITHUB_RUN_NUMBER}</bundle.version>
<bundle.short.version>1.0</bundle.short.version>
</properties>
</profile>
<profile>
<id>android</id>
<properties>
<gluonfx.target>android</gluonfx.target>
<app.identifier>com.audioTest</app.identifier>
<version.code>${env.GITHUB_RUN_NUMBER}</version.code>
<provided.keystore.path>${env.GLUON_ANDROID_KEYSTOREPATH}</provided.keystore.path>
<provided.keystore.password>${env.GLUON_ANDROID_KEYSTORE_PASSWORD}</provided.keystore.password>
<provided.key.alias>${env.GLUON_ANDROID_KEYALIAS}</provided.key.alias>
<provided.key.alias.password>${env.GLUON_ANDROID_KEYALIAS_PASSWORD}</provided.key.alias.password>
</properties>
</profile>
<profile>
<id>pi</id>
<properties>
<gluonfx.target>linux-aarch64</gluonfx.target>
</properties>
</profile>
</profiles>
</project>

文件位于src/main/resources/sounds/Beep.wav目录下。

例外:

D/GraalCompiled( 7319): Exception in thread "JavaFX Application Thread" java.util.NoSuchElementException: No value present
D/GraalCompiled( 7319):         at java.util.Optional.orElseThrow(Optional.java:377)
D/GraalCompiled( 7319):         at com.audioTest.Notifier$MobileNotifier.<init>(Notifier.java:21)
D/GraalCompiled( 7319):         at java.util.Optional.map(Optional.java:260)
D/GraalCompiled( 7319):         at com.audioTest.Notifier.create(Notifier.java:11)
D/GraalCompiled( 7319):         at com.audioTest.MyApplication$1HomeView.lambda$new$0(MyApplication.java:22)
D/GraalCompiled( 7319):         at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
D/GraalCompiled( 7319):         at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
D/GraalCompiled( 7319):         at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
D/GraalCompiled( 7319):         at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
D/GraalCompiled( 7319):         at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)D/GraalCompiled( 7319):         at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
D/GraalCompiled( 7319):         at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)D/GraalCompiled( 7319):         at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
D/GraalCompiled( 7319):         at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)D/GraalCompiled( 7319):         at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
D/GraalCompiled( 7319):         at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)D/GraalCompiled( 7319):         at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
D/GraalCompiled( 7319):         at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
D/GraalCompiled( 7319):         at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
D/GraalCompiled( 7319):         at javafx.event.Event.fireEvent(Event.java:198)
D/GraalCompiled( 7319):         at javafx.scene.Node.fireEvent(Node.java:8797)
D/GraalCompiled( 7319):         at javafx.scene.control.Button.fire(Button.java:203)
D/GraalCompiled( 7319):         at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:208)
D/GraalCompiled( 7319):         at com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
D/GraalCompiled( 7319):         at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:247)
D/GraalCompiled( 7319):         at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
D/GraalCompiled( 7319):         at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
D/GraalCompiled( 7319):         at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
D/GraalCompiled( 7319):         at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
D/GraalCompiled( 7319):         at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)D/GraalCompiled( 7319):         at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
D/GraalCompiled( 7319):         at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)D/GraalCompiled( 7319):         at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
D/GraalCompiled( 7319):         at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)D/GraalCompiled( 7319):         at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
D/GraalCompiled( 7319):         at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)D/GraalCompiled( 7319):         at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
D/GraalCompiled( 7319):         at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
D/GraalCompiled( 7319):         at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
D/GraalCompiled( 7319):         at javafx.event.Event.fireEvent(Event.java:198)
D/GraalCompiled( 7319):         at javafx.scene.Scene$MouseHandler.process(Scene.java:3881)
D/GraalCompiled( 7319):         at javafx.scene.Scene.processMouseEvent(Scene.java:1874)
D/GraalCompiled( 7319):         at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2607)
D/GraalCompiled( 7319):         at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:411)
D/GraalCompiled( 7319):         at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:301)
D/GraalCompiled( 7319):         at java.security.AccessController.executePrivileged(AccessController.java:169)
D/GraalCompiled( 7319):         at java.security.AccessController.doPrivileged(AccessController.java:399)
D/GraalCompiled( 7319):         at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:450)
D/GraalCompiled( 7319):         at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:424)
D/GraalCompiled( 7319):         at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:449)
D/GraalCompiled( 7319):         at com.sun.glass.ui.View.handleMouseEvent(View.java:551)
D/GraalCompiled( 7319):         at com.sun.glass.ui.View.notifyMouse(View.java:937)
D/GraalCompiled( 7319):         at com.sun.glass.ui.monocle.MonocleView.notifyMouse(MonocleView.java:116)
D/GraalCompiled( 7319):         at com.sun.glass.ui.monocle.MouseInput.notifyMouse(MouseInput.java:328)
D/GraalCompiled( 7319):         at com.sun.glass.ui.monocle.MouseInput.lambda$postMouseEvent$3(MouseInput.java:241)
D/GraalCompiled( 7319):         at com.sun.glass.ui.monocle.RunnableProcessor.runLoop(RunnableProcessor.java:92)
D/GraalCompiled( 7319):         at com.sun.glass.ui.monocle.RunnableProcessor.run(RunnableProcessor.java:51)
D/GraalCompiled( 7319):         at java.lang.Thread.run(Thread.java:833)
D/GraalCompiled( 7319):         at com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:704)
D/GraalCompiled( 7319):         at com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:202)

这是一个很好的方法。不需要更改Media模块依赖项中的任何内容,因为它不会在具有Attach服务的移动设备上使用。

对于不自动支持的每个资源,都需要在<resourceList>中声明,这也是错误的来源。它需要指定一个路径,只有当文件直接在resources文件夹下时,才可以指定<item>Beep.wav</item>。要查找该文件,需要使用以下模式:<item>.*/Beep.wav$</item>.

来源:https://github.com/oracle/graal/blob/master/docs/reference-manual/native-image/Resources.md