在 Windows 和 Mac 的網頁上喚起 JavaFX 應用

weixin_34007291發表於2017-11-21

前言

由於工作原因,接觸一些 JavaFX 開發。最近產品的同學提到一個遺留已久的問題——在網站啟動我們的 JavaFX 本地應用程式。於是就著手瞭解一下。曾經做過 Android 應用程式的 web 喚起功能。大致就是註冊一個自定義的 Scheme,在 web 中嵌入這個自定義 Scheme 的 URL 喚起本地應用,就像 http://https:// 的 URL 可以喚起瀏覽器。桌面應用也可以通過同樣的方式來啟動。下面簡單的記錄一下實現過程。

以下文章中的程式碼可以在此免費下載:javafx-custom-url-scheme。專案使用 maven 管理,使用 javafx-maven-plugin 外掛打包 native 程式。結構如下:

├── README.md
├── javafx-custom-url-scheme.iml
├── pom.xml
└── src
    └── main
        ├── deploy
        │   └── package
        │       ├── macosx
        │       └── windows
        ├── java
        │   └── org
        │       └── eirture
        │           └── javafx
        │               └── App.java
        └── resources

通過 URL 喚起 JavaFX Windows 應用

通過這篇文件 Registering an Application to a URI Scheme,我們知道通過自定義 URI Scheme 喚起本地應用,需要新增如下注冊表資訊:

HKEY_CLASSES_ROOT
   myapp    # 自定義協議的名稱
      (Default) = "URL:myapp"
      URL Protocol = ""
      DefaultIcon    # 定義圖示
         (Default) = "javafx-custom-url-scheme.exe,1"  # 圖示路徑,‘path,iconIndex’形式
      shell
         open
            command
               (Default) = "C:\Program Files\javafx-custom-url-scheme.exe" "%1"    # 定義應用程式路徑地址, %1 表示接收一個引數。

下一步我們需要考慮在 JavaFX 程式中將此資訊寫入登錄檔。使用 javapackager 打包,我們會發現,windows 平臺的 .exe 程式使用的是 InnoSetup,打包過程輸出的資訊提示我們,可以通過
project/deploy/package/windows/AppName.iss 檔案自定義打包的配置資訊,在 InnoSetup 幫助文件中不難找到新增登錄檔資訊的方法。

第一個問題,我們從哪裡獲取 AppName.iss 配置檔案呢。通過打包過程輸出資訊,我們發現,在 ~/AppData/Local/Temp/fxbundler* 目錄下有我們要找的檔案。不過這裡面是根據我們配置的 javafxpackage 打包資訊生成的配置檔案,如果直接 Copy 過來,那麼這些打包資訊就不能根據我們的配置生成了。其實我們找到這個生成配置資訊的模版檔案放在此處即可: template.iss。將 template.iss 放置 javafx-custom-url-scheme/src/main/deploy/package/windows/MyApp.iss,增加配置資訊如下:

[Registry]
Root: HKCR; Subkey: "myapp"; Flags: uninsdeletekey; ValueType: string; ValueData: "URL:myapp"
Root: HKCR; Subkey: "myapp"; Flags: uninsdeletekey; ValueType: string; ValueName: "URL Protocol"; ValueData: "";
Root: HKCR; Subkey: "myapp\DefaultIcon"; Flags: uninsdeletekey; ValueType: expandsz; ValueData: "APPLICATION_NAME.exe,1"
Root: HKCR; Subkey: "myapp\shell\open\command"; Flags: uninsdeletekey; ValueType: expandsz; ValueData: "{app}\APPLICATION_NAME.exe %1";

開始打包 native 應用程式(需要先確保已經安裝了 innoSetup)

mvn clean jfx:native

在專案的 target\jfx\native\ 目錄下將會生成我們的應用程式 MyApp.exe,安裝後會自動在登錄檔中寫入自定義的 scheme 資訊。如下圖所示:

638418-3704666ac32b3964.png
windows 登錄檔資訊

現在就可以在瀏覽器輸入 myapp:// 啟動 MyApp.exe 應用程式了。

638418-546a30bfc8bbd4a0.png
web 喚起 MyApp.exe

通過 URL 喚起 JavaFX Mac OS 應用

MacOS 應用程式如何自定義 URL Scheme 的教程很多,在此我們也不過多介紹。需要在應用的 Info.plist 檔案中新增如下配置:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>org.eirture.javafx.App</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>myapp</string>
        </array>
    </dict>
</array>

我們需要解決的問題是,使用 javafxpackager 打包 JavaFX 應用程式時,如何在 Info.plist 檔案中新增這些資訊。在執行 mvn jfx:native 命令打包過程中,可以看到控制檯輸出瞭如下資訊:

...
Building DMG package for MyApp
正在準備 Info.plist: /var/folders/vy/hfcccjpx0xz3q53dvj32llmm0000gn/T/fxbundler8587227884586677537/macosx/Info.plist
  Using default package resource [包配置檔案]  (add package/macosx/Info.plist to the class path to customize)
  Using default package resource [icon]  (add package/macosx/MyApp.icns to the class path to customize)
Running [security, find-certificate, -c, Developer ID Application: , -a]
...

告訴我們將自定義的 Info.plist 檔案放至 package/macosx/Info.plist 可以實現自定義。同 windows 下,我們需要放入模版檔案,並在模版檔案中加入我們自定義的配置資訊。在 openjfx 專案中可以找到 Info-lite.plist.template 檔案,並在檔案中加入我們自定義的配置。

<?xml version="1.0" ?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>LSMinimumSystemVersion</key>
        <string>10.7.4</string>
        <key>CFBundleDevelopmentRegion</key>
        <string>English</string>
        <key>CFBundleAllowMixedLocalizations</key>
        <true/>
        <key>CFBundleExecutable</key>
        <string>DEPLOY_LAUNCHER_NAME</string>
        <key>CFBundleIconFile</key>
        <string>DEPLOY_ICON_FILE</string>
        <key>CFBundleIdentifier</key>
        <string>DEPLOY_BUNDLE_IDENTIFIER</string>
        <key>CFBundleInfoDictionaryVersion</key>
        <string>6.0</string>
        <key>CFBundleName</key>
        <string>DEPLOY_BUNDLE_NAME</string>
        <key>CFBundlePackageType</key>
        <string>APPL</string>
        <key>CFBundleShortVersionString</key>
        <string>DEPLOY_BUNDLE_SHORT_VERSION</string>
        <key>CFBundleSignature</key>
        <string>????</string>
        <!-- See http://developer.apple.com/library/mac/#releasenotes/General/SubmittingToMacAppStore/_index.html
             for list of AppStore categories -->
        <key>LSApplicationCategoryType</key>
        <string>DEPLOY_BUNDLE_CATEGORY</string>
        <key>CFBundleVersion</key>
        <string>DEPLOY_BUNDLE_CFBUNDLE_VERSION</string>
        <key>NSHumanReadableCopyright</key>
        <string>DEPLOY_BUNDLE_COPYRIGHT</string>
        <key>JVMRuntime</key>
        <string>DEPLOY_JAVA_RUNTIME_NAME</string>
        <key>JVMMainClassName</key>
        <string>DEPLOY_LAUNCHER_CLASS</string>
        <key>JVMAppClasspath</key>
        <string>DEPLOY_APP_CLASSPATH</string>
        <key>JVMMainJarName</key>
        <string>DEPLOY_MAIN_JAR_NAME</string>
        <key>JVMPreferencesID</key>
        <string>DEPLOY_PREFERENCES_ID</string>
        <key>JVMOptions</key>
        <array>
            DEPLOY_JVM_OPTIONS
        </array>
        <key>JVMUserOptions</key>
        <dict>
            DEPLOY_JVM_USER_OPTIONS
        </dict>
        <key>ArgOptions</key>
        <array>
            DEPLOY_ARGUMENTS
        </array>DEPLOY_FILE_ASSOCIATIONS
        <key>NSHighResolutionCapable</key>
        <string>true</string>

        <!-- custom configuration -->
        <key>CFBundleURLTypes</key>
        <array>
            <dict>
                <key>CFBundleURLName</key>
                <string>org.eirture.javafx.App</string>
                <key>CFBundleURLSchemes</key>
                <array>
                    <string>myapp</string>
                </array>
            </dict>
        </array>
    </dict>
</plist>

重新打包安裝,即可在瀏覽器通過 myapp:// 喚起我們應用程式了。

638418-396601507ec3e983.png
Web 喚起 MyApp.app

總結

終於我們實現了從 web 頁面喚起 JavaFX 本地應用程式功能。各個平臺上實現自定義的 URL Scheme 的教程都很多,在此主要是想分享使用 JavaFX 開發,如何配置此功能。當然,這裡僅僅是通過自定義的 URL Scheme 喚起本地應用程式。我們還可以使用自定義的 URLSchemeHandler 接收來自 URL Scheme 的引數,例如通過 myapp://user/eirture 給本地應用傳遞 user/eirture 資訊等。

相關文章