プロジェクト

全般

プロフィール

JavaFXライブラリのインストール

はじめに

Java SE 11以降、JavaFXはライブラリとして別途インストールするようになりました。本ページは、Java SE 11以降でJavaFXライブラリを利用した開発をするためのインストール作業を記述します。

JavaFXは、OSネイティブなライブラリを含むため、OS別にリリースされたライブラリを入手しインストールします。

JavaFX同梱のOpenJDKもある

OpenJDKのバイナリはいくつかの組織から提供されており、中にはJavaFXを同梱したものがあります。これを使えばJavaFXを別途インストールする必要はなくなります。
OpenJDKに記載。

Windows用のJDKとJavaFXのネイティブビルドの差異が懸念

Windows版では、OpenJDKのバイナリが使用するVC++ランタイムバージョンとJavaFXライブラリのバイナリが使用するVC++ランタイムバージョンが違うと組み合わせたときの動作が大丈夫かなという心配があります。原則、OpenJDKをビルドしたVisual C++バージョンとJavaFXをビルドしたVisual C++バージョンがメジャーバージョンで一致している必要があります。ただ、次の記事によれば、Visual C++ 2015以降であればメジャーバージョンが異なっても互換性があるようです。

https://docs.microsoft.com/ja-jp/cpp/porting/binary-compat-2015-2017?view=msvc-160

入手

JavaFXは、現在次のリリースがあります。

  1. OpenJFXのダウンロードサイト
  2. Gluon社のダウンロードサイト
  3. mavenリポジトリ

OpenJFXのダウンロードサイトから入手

OpenJFXサイト(以下URL)からビルド済みのJavaFXライブラリをダウンロードします。
https://openjfx.io/

現時点(2018-10-11)では、ダウンロードのリンク先がGluon社のJavaFXサイトとなっており、そこからOS毎のライブラリをダウンロードするようになっています。

Gluon社のダウンロードサイトから入手

https://gluonhq.com/products/javafx/
各OS毎(Windows、Mac、Linux)に、SDKとjmodsと2つのアーカイブが提供されます。
SDKには、OSネイティブのライブラリ、JARファイルが含まれています。
jmodsには、Javaモジュールファイル(jmod)が含まれています。jmodファイルはコンパイル・リンク用に使うためのもので、jmodファイル内にOSネイティブのライブラリも含まれています。jmodファイルは実行時には使えません。

アプリケーション実行用のイメージ(アプリケーションモジュールとその実行に必要とするJavaモジュールを抜粋したアプリケーション実行環境)を作成しないのであれば前者のSDKだけ入手すれば十分です。イメージを作成する場合はSDKに加えて後者のjmodsも入手します。

Windows版の入手とインストール

JavaFXライブラリを、C:\Program Files\Java\JavaFXの下に置くこととします。

Gluon社のダウンロードサイトから次をダウンロードします。
次は、JavaFX 11.0.2の場合です。

名称 ファイル名
JavaFX Windows SDK openjfx-11.0.2_windows-x64_bin-sdk.zip
JavaFX Windows jmods openjfx-11.0.2_windows-x64_bin-jmods.zip

各zipファイルを、C:\Program Fils\Java\JavaFXの下に展開します。

C:\Program Files\Java\JavaFX
  +-- javafx-sdk-11.0.2
  |     +-- bin
  |     |     +-- *.dll
  |     +-- legal
  |     +-- lib
  |           +-- *.jar
  +-- javafx-jmods-11.0.2
        +-- *.jmod

JavaFX APIドキュメントの入手

JavaFX のAPIドキュメントはオンラインでは次のURLから[Documentation]リンクを辿って参照できます。
https://openjfx.io/

オフラインで利用できるようダウンロードするには、次のGithubリポジトリからコンテンツをダウンロードします。

上述URLをブラウザで開き、[Clone or download] > [Download ZIP] でダウンロードできます。

動作確認

ライブラリをインストールしたら、動作確認のため簡単なプログラムをビルド・実行します。

空白のウィンドウを表示するプログラム

作業ディレクトリの作成

作業ディレクトリを用意し、ソースコードを置くディレクトリ、コンパイル結果を置くディレクトリを作ります。

SimpleApp
  +-- build
  +-- src
        +-- com.torutk.simplefx          <-- モジュール名に一致する名前
              +-- com                    <-- パッケージ名に基づくディレクトリ名
                    +-- torutk           <-- パッケージ名に基づくディレクトリ名
                          +-- simplefx   <-- パッケージ名に基づくディレクトリ名

ソースファイルを作成

  • src\com.torutk.simplefx\com\torutk\simplefx\SimpleApp.java
    package com.torutk.simplefx;
    
    import javafx.application.Application;
    import javafx.stage.Stage;
    
    public class SimpleApp extends Application {
        @Override
        public void start(Stage primaryStage) {
        primaryStage.show();
        }
    }
    

モジュール記述子を作成

  • src\com.torutk.simplefx\module-info.java
    module hellofx {
        requires javafx.controls;  // (1)
        opens com.torutk.hello to javafx.graphics;  // (2)
    }
    
  • (1)
    今回のソースコードから利用するJavaFXのAPIは、javafx.graphicsモジュールに含まれますが、アプリケーションからは通常はjavafx.controlsモジュールへの依存を定義します。javafx.controlsは、javafx.graphicsへの依存(推移的依存)を持つので、アプリケーションプログラム側のモジュール記述子にはjavafx.controlsモジュールへの依存を明示的に定義することでjavafx.graphicsへの依存も定義され、その中のクラスを利用できるようになります。
  • (2)
    アプリケーションプログラム側で定義したJavaFXのApplication派生クラスは、実行時にJavaFXライブラリ側でリフレクションを使ってインスタンス化されます。そのため、実行時にJavaFXライブラリ(の中のjavafx.graphicsモジュール)からアプリケーションプログラム側のApplication派生クラスのパッケージに対してリフレクションを許可するopens指定をモジュール記述子に定義します。

ソースファイルの構成

この時点で作業ディレクトリには次の様にファイルが置かれます。

SimpleApp
  +-- build
  +-- src
        +-- com.torutk.simplefx
              +-- module-info.java
              +-- com
                    +-- torutk
                          +-- simplefx
                                +-- SimpleApp.java

コンパイル

D:\work\SimpleApp> javac -d build -p "C:\Program Files\Java\JavaFX\javafx-sdk-11.0.2\lib" --module-source-path src -m com.torutk.simplefx

D:\work\SimpleApp> 
  • -dオプションでコンパイル結果出力先ディレクトリを指定
  • -pオプションでJavaFXライブラリ(JARファイルまたはjmodファイル)が収められた場所を指定
    -pは、--module-pathの省略形
  • --module-source-pathでコンパイルするモジュールのソースファイルが収められた場所を指定
  • -mオプションでコンパイルするモジュールを指定
    -mは、--module-nameの省略形

コンパイル結果は次のようになります。

SimpleApp
  +-- build
        +-- com.torutk.simplefx
              +-- module-info.class
              +-- com
                    +-- torutk
                          +-- simplefx
                                +-- SimpleApp.class

コンパイル結果の実行

D:\work\SimpleApp> java -p build;"C:\Program Files\Java\JavaFX\javafx-sdk-11.0.2\lib" -m com.torutk.simplefx/com.torutk.simplefx.SimpleApp

モジュールJARファイルの作成

先のビルド結果から、モジュールJARファイルを生成します。

まず、モジュールJARファイルを格納するディレクトリを作成しておきます。

SimpleApp
  +-- build
        +-- mods

D:\work\SimpleApp> jar cfe build\mods\com.torutk.simplefx.jar com.torutk.simplefx.SimpleApp -C build\com.torutk.simplefx .
  • cfオプションで新たにJARファイルを指定したパスで生成
    cオプションは、--createオプションの超短縮形(UNIXのtarコマンドオプション流儀)です。
    fオプションは、--fileオプションの超短縮形(同上)です。
  • eオプションでエントリポイントのクラスを指定
    eオプションは、--main-classオプションの超短縮形です。

モジュールJARファイルの実行

D:\work\SimpleApp> java -p build\mods;"C:\Program Files\Java\JavaFX\javafx-sdk-11.0.2\lib" -m com.torutk.simplefx

モジュールJARファイルの生成時にエントリポイントを指定したので、-mオプションではモジュール名だけ指定します。

リンク(アプリケーション実行用のイメージ作成)

アプリケーションを実行するのに必要なモジュールだけを抜粋したJava実行環境を作成します。

D:\work\SimpleApp> jlink -p build;"C:\Program Files\Java\JavaFX\javafx-jmods-11.0.2" --add-modules com.torutk.simplefx --launcher simplefx=com.torutk.simplefx/com.torutk.simplefx.SimpleApp --output simplefx-image

少し時間がかかりますが、成功すると--outputオプションで指定したディレクトリの下に、アプリケーションプログラムとその実行に必要なJavaの実行環境が生成されます。ディレクトリ構造を次に示します。

SimpleApp
  +-- simplefx-image
        +-- lib
        |     +-- security
        |     +-- server
        +-- conf
        |     +-- security
        |           +-- policy
        |                 +-- limited
        |                 +-- unlimited
        +-- include
        |     +-- win32
        +-- bin
        |     +-- server
        +-- legal

binディレクトリの下に、jlinkの--launcherオプションで指定した起動スクリプト(simplefxおよびsimplefx.bat)が生成されます。これを実行するとアプリケーションが起動します。

D:\work\SimpeApp> simplefx-image\bin\simplefx

FXMLで定義した画面(テキストメッセージ)を表示するプログラム

作業ディレクトリの作成

作業ディレクトリを用意し、ソースコードを置くディレクトリ、コンパイル結果を置くディレクトリを作ります。

SimpleFxmlApp
  +-- build
  +-- src
        +-- com.torutk.simplefx          <-- モジュール名に一致する名前
              +-- com                    <-- パッケージ名に基づくディレクトリ名
                    +-- torutk           <-- パッケージ名に基づくディレクトリ名
                          +-- simplefx   <-- パッケージ名に基づくディレクトリ名

ソースファイルを作成

JavaFXアプリケーションクラス
  • src\com.torutk.simplefx\com\torutk\simplefx\SimpleFxmlApp.java
    package com.torutk.simplefx;
    
    import javafx.application.Application;
    import javafx.stage.Stage;
    
    public class SimpleFxmlApp extends Application {
    
        @Override
        public void start(Stage primaryStage) {
        var root = FXMLLoader.load(getClass().getResource("SimpleView.fxml"));
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
        }
    }
    
FXMLファイル
  • src\com.torutk.simplefx\com\torutk\simplefx\SimpleView.fxml
    <?import javafx.scene.layout.GridPane?>
    <?import javafx.scene.control.Label?>
    
    <GridPane xmlns:fx="http://javafx.com/fxml" alignment="center">
      <Label text="Hello, JavaFX" />
    </GridPane>
    

モジュール記述子を作成

  • src\com.torutk.simplefx\module-info.java
    module com.torutk.simplefx {
        requires javafx.controls;
        requires javafx.fxml;
        opens com.torutk.simplefx to javafx.graphics, javafx.fxml;
    }
    

ソースファイルの構成

この時点で作業ディレクトリには次の様にファイルが置かれます。

SimpleFxmlApp
  +-- build
  +-- src
        +-- com.torutk.simplefx
              +-- module-info.java
              +-- com
                    +-- torutk
                          +-- simplefx
                                +-- SimpleFxmlApp.java
                                +-- SimpleView.fxml

コンパイル

D:\work\SimpleFxmlApp> javac -d build -p "C:\Program Files\Java\JavaFX\javafx-sdk-11.0.2\lib" --module-source-path src -m com.torutk.simplefx

D:\work\SimpleFxmlApp> 

FXMLファイルを、アプリケーションクラスと同じ場所にコピーします。

D:\work\SimpleFxmlApp> copy src\com.torutk.simplefx\com\torutk\simplefx\SimpleView.fxml build\com.torutk.simplefx\com\torutk\ximplefx\
  1 個のファイルをコピーしました。
D:\work\SimpleFxmlApp> 

コンパイル結果の実行

D:\work\SimpleFxmlApp> java -p build;"C:\Program Files\Java\JavaFX\javafx-sdk-11.0.2\lib" -m com.torutk.simplefx/com.torutk.simplefx.SimpleFxmlApp

トラブルシューティング

Application派生クラスで実行時例外(InvocationTargetException)

モジュール記述子に、javafx.graphicsモジュールからApplication派生クラスのパッケージに対するリフレクションを許可するopens定義を記述しなかった場合、実行時に次のエラーが発生します。

Exception in Application constructor
Exception in thread "main" java.lang.reflect.InvocationTargetException
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1051)
Caused by: java.lang.RuntimeException: Unable to construct Application instance: class com.torutk.hello.Hello
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:890)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
        at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.IllegalAccessException: class com.sun.javafx.application.LauncherImpl (in module javafx.graphics) cannot access class com.torutk.hello.Hello (in module hellofx) because module hellofx does not export com.torutk.hello to module javafx.graphics
        at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:361)
        at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:591)
        at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$8(LauncherImpl.java:802)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:455)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
        at java.base/java.security.AccessController.doPrivileged(Native Method)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
        at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
        at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
        at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
        ... 1 more

javafx.graphicsモジュール(Applicationクラスを含むモジュール)が、Application派生クラスへリフレクションを使ったアクセスをする際にエラーとなっています。
モジュール記述子でアプリケーションのモジュールを公開する必要があります。

次の記述を追加します。

  opens com.torutk.hello to javafx.graphics;

jlinkで生成したイメージの実行時例外

jlinkで実行イメージを生成する際に、JavaFXのSDKを指定した場合、つまりモジュールJARファイルのある場所を指定した場合、次のエラーが実行時に発生しました。

D:\work\hello> hellofx-image\bin\hello
Graphics Device initialization failed for :  d3d, sw
Error initializing QuantumRenderer: no suitable pipeline found
java.lang.RuntimeException: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pipeline found
        at javafx.graphics/com.sun.javafx.tk.quantum.QuantumRenderer.getInstance(QuantumRenderer.java:280)
        at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.init(QuantumToolkit.java:222)
        at javafx.graphics/com.sun.javafx.tk.Toolkit.getToolkit(Toolkit.java:260)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:267)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:158)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:658)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:409)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:363)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1051)
Caused by: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pipeline found
        at javafx.graphics/com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.init(QuantumRenderer.java:94)
        at javafx.graphics/com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:124)
        at java.base/java.lang.Thread.run(Thread.java:834)
Exception in thread "main" java.lang.reflect.InvocationTargetException
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1051)
Caused by: java.lang.RuntimeException: No toolkit found
        at javafx.graphics/com.sun.javafx.tk.Toolkit.getToolkit(Toolkit.java:272)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:267)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:158)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:658)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:409)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:363)
        ... 5 more

JARファイルのある場所を指定して実行イメージを生成すると、ネイティブライブラリが展開されません。
解決には、jmodファイルのある場所を指定する必要があります。jmodファイルにはネイティブライブラリが含まれているので、生成された実行イメージにはネイティブライブラリが含まれます。

FXMLファイル読み込みの実行時例外

FXMLファイルをコピーし忘れた場合に発生します。

Exception in Application start method
Exception in thread "main" java.lang.reflect.InvocationTargetException
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)
        at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1051)
Caused by: java.lang.RuntimeException: Exception in Application start method
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:900)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
        at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.NullPointerException: Location is required.
        at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3230)
        at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3194)
        at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3163)
        at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3136)
        at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3113)
        at javafx.fxml/javafx.fxml.FXMLLoader.load(FXMLLoader.java:3106)
        at com.torutk.simplefx/com.torutk.simplefx.SimpleFxmlApp.start(SimpleFxmlApp.java:13)
        at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:846)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:455)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
        at java.base/java.security.AccessController.doPrivileged(Native Method)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
        at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
        at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
        at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
        ... 1 more


ほぼ3年前に更新