Java/Shell程序封装为MacOS程序 Application

macOS下有些带GUI的程序并没有按照macOS的规范去打包成一个MacOS的Application,导致启动它的体验不太好,虽然不影响使用。

这里以Jadx-- 一个Java反编译工具, 为例演示如何把一个Java程序包装为MacOS程序 Application。

通过brew安装过jadx之后,可以通过执行jadx-gui命令来启动他的图形界面。

做个图标

做为一个应用程序,最早呈现给用户的就是图标。macOS 使用 icns 格式的图标资源。图片格式及大小没具体要求,长宽比例必须是1:1的。
* 可以通过cloudconvert.com在线转换 https://cloudconvert.com/png-to-icns
* 可以通过makeicns线下转换

改进Java显示效果

Java的GUI启动之后,在dock里还是显示JRE的默认图标,如果需要额外定制图标就需要通过其他参数指定。
Java为macOS提供两个专门的参数来定制在macOS的表现(在macOS上可以通过java -X 命令查看参数描述)。

-Xdock:name 用来指定在菜单栏里显示的名字。在macOS早期的版本是可以定制在dock中显示的名字。现在不行了。
-Xdock:icon= 用来指定在dock中显示的图标。
jadx-gui 支持通过JADX_GUI_OPTS变量给他传参数,其他Java程序按自己实际情况调整。

Automator

Automator 是macOS自带的自动化工具。
1. 启动 Automator 并创建一个 Application 类型的文件。
2. 添加一个Run Shell Script的action。Pass input改为as arguments并在下方的文本框内填入如下命令

  1. 点击文件-保存。名字为Jadx.app, 位置选/Application,文件类型为Application
  2. 把定制的图标重命名为ApplicationStub.icns,然后覆盖位于/Application/Jadx.app/Contents/Resources/ApplicationStub.icns的Automator默认图标。
    > 在 Xxx.app 上点右键,选择 显示包内容(Show Package Contents),就可以以文件夹的方式打开Xxx.app了。

universalJavaApplicationStub

使用 Automator 包装的程序制作简单,唯独在dock中显示的名字不能修改,略有遗憾。参考了JD-GUI的实现,我发现了universalJavaApplicationStub

miniBundleApp

最小可运行的macOS程序结构大概是这个样子。
* Info.plist 存储程序相关的属性配置信息。
* MacOS 目录存放可执行程序
* Resources 存放资源文件, 示例中放了图标资源。而像JD-GUI就把jar包放在Resources/Java目录下。

  1. 创建目录结构。

  1. 配置Info.plist
    Info.plist 位于 Demo.app/Contents/Info.plist
    主要关系几个属性:CFBundleName、CFBundleIdentifier、MainClass、ClassPath。

* CFBundleName 程序名称
* CFBundleIdentifier 程序唯一标识
* MainClass Java的启动class
* ClassPath Java的classpath路径。要注意的是如果要配置目录时,lib/* 需要对*做转义lib/\\*

  1. 跟 Automator 一样替换图标文件即可。

封装依赖

之前做的这些更准确的说是包装而不是封装,因为我们的程序极度的依赖外部的文件,这个Application 打包给别人并不能做为独立应用直接使用。如果希望能做为独立应用还需要做以下工作:

程序内添加应用依赖

本文整体都是以 Apple 风格的Java程序,如果希望以Oracle风格构建Application可以查看universalJavaApplicationStub文档及源码。

Resources目录下创建Java目录。Demo.app/Contents/Resources/Java,然后把jadx所有依赖的jar包复制到这个目录下。
然后配置classpath为$JAVAROOT/\\*

打包JRE

如果需要把JRE一起打包进来用jlink(jdk9+) 或 jpackage(jdk8)更合适。
universalJavaApplicationStub的JAVA_HOME支持相对路径。在Info.plist内配置LSEnvironment并复制JRE到对应位置就可以了。

macOS 10.15+ 可能会遇到的问题

从macOS 10.15开始,macOS 默认会阻止对受保护资源的访问,如用户的下载、文档或桌面文件夹,并显示一个安全对话框,用户必须接受该对话框才能被允许访问。

当在你的应用程序中使用javax.swing.JFileChooser时,它支持这些类型的安全对话框(有趣的是java.awt.FileDialog不支持!),你应该使用universalJavaApplicationStub脚本的编译二进制文件而不是普通的bash脚本。

二进制文件有两个不同的版本,根据自己的系统选择。 universalJavaApplicationStub-xxx-binary-macos-10.15.zip 的可能可以在 macOS 11 上使用, universalJavaApplicationStub-xxx-binary-macos-11.0.zip 肯定不能在 10.15 的系统上使用。

打开方式 Open With

如果希望在class、jar、java、zip等文件上点右键能出现该程序,只需要在Info.plist里添加如下代码:

universalJavaApplicationStub-Info.plist
https://gist.github.com/cnxobo/e53e0571c5165913e68c8db3600c7d29

评论关闭。