在 Windows 和 Mac 的网页上唤起 JavaFX 应用

前言

由于工作原因,接触一些 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 信息。如下图所示:

windows 注册表信息

现在就可以在浏览器输入 myapp:// 启动 MyApp.exe 应用程序了。

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:// 唤起我们应用程序了。

Web 唤起 MyApp.app

总结

终于我们实现了从 web 页面唤起 JavaFX 本地应用程序功能。各个平台上实现自定义的 URL Scheme 的教程都很多,在此主要是想分享使用 JavaFX 开发,如何配置此功能。当然,这里仅仅是通过自定义的 URL Scheme 唤起本地应用程序。我们还可以使用自定义的 URLSchemeHandler 接收来自 URL Scheme 的参数,例如通过 myapp://user/eirture 给本地应用传递 user/eirture 信息等。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容