Java 快速開發幾 MB 獨立 EXE,寫圖形介面很方便

2022-09-17 15:00:33

Java 寫的桌面軟體帶上執行時只有 6 MB,而且還是獨立 EXE 文 件,是不是難以置信?

想一想 Electron 沒寫多少功能就可能超過百 MB 的體積,Java 寫的桌面軟體算不算得上小、輕、快呢?

aardio 可以支援很多程式語言, Java 就是其中之一。

aardio + Java 可生成體積極小的獨立 EXE 檔案,可以記憶體載入 Java 編寫的 class 檔案,用 aardio + Java 生成的軟體,釋出時不需要攜帶 Java 執行時。

aardio 寫圖形介面很方便。例如僅 605 KB 的開源軟體 ImTip


例如僅 755 KB 的開源軟體 Gif123:

aardio 開發的程式,相容XP,Vista,Win7,Win8,Win10,Win11...... 等所有系統。aardio 只支援 Windows 系統( macOS + Linux 桌面市場份額小於被遺忘的 Win8 ),同時也扔掉了跨平臺的沉重包袱(更輕量、更好地利用專用平臺優勢)。將需要跨平臺的程式碼用 Java 寫,這並不影響在 Windows 上可以使用 aardio 生成 EXE 檔案 。跨平臺與專有平臺工具並不衝突,可以相互結合。

▶ 開始

預設只要在 aardio 中呼叫

import java; 

就會自動搜尋系統可用的 JRE,如果沒有找到 JRE,會自動匯入 java.jre.v8 擴充套件庫,並自動安裝 OpenJDK JRE v8 。

如果您希望釋出的軟體自帶 Java 執行時,或者指定 JRE 的版本,只要在 aardio 中匯入其他版本 JRE 的擴充套件庫就可以,例如執行

import java.jre.v8ora

就可以自動繫結 Oracle Java 8 執行時,軟體執行會自動查詢使用者電腦上符合要求的 JRE,如果沒有找到會全自動地安裝和部署好,開發者要做的,僅僅就是寫幾句程式碼,把 EXE 簡單地分發給使用者就可以了。

當然,獨立 EXE 也可以嵌入更小的 JRE,aardio 裡提供了一個範例 JRE,只要簡單的在程式碼中加入

import java.jre.v6

就可以在軟體中嵌入一個精簡版的 JRE 6 執行時,這個 JRE 只有 6MB 多一點,而且可以生成獨立 EXE 檔案,甚至還能支援 Windows XP 這種古老的系統。Java 寫的桌面軟體帶上執行時只有 6 MB,而且還是獨立 EXE 文 件,是不是難以置信?!想一想 Electron 沒寫多少功能就可能超過百 MB 的體積,Java 寫的桌面軟體算不算得上小、輕、快呢?!

現代 Java 已經可以非常方便地生成精簡版的 JRE,所以你只要參考 java.jre.v6 的實現( 放心這個庫的原始碼只有幾行 ),就可以非常輕鬆地更換為其他 JRE 版本,並生成極小的 EXE檔案( 而且可以是獨立的 EXE檔案)。

好吧,我們來看幾個 aardio 自帶的 aardio + Java 範例( 可以直接執行 )。

▶ aardio 呼叫 Java 函數

首先請看下面 aardio 呼叫 Java 如此簡單,我們可以在 aardio 中直接匯入 Java 的類,建立 Java 物件,就像在 Java 程式碼中一樣使用它們。

import console;
import java;

//建立 Java 虛擬機器器
var jvm = java();

//匯入 Java 類
HelloworldApp = jvm.import( "aardio.sample.HelloworldApp");

//修改類的靜態成員變數
HelloworldApp.static_num = 996;

//獲取類的靜態成員變數
console.log( HelloworldApp.static_num );

//直接用 Java 類建立 Java 物件,跟 Java 中一樣用就行
var helloworld = HelloworldApp();

//獲取物件的成員欄位值
console.log( helloworld.name );

//呼叫物件的方法
console.log( helloworld.test(1) );

//呼叫 main 函數,引數是字串陣列
var ret = HelloworldApp.main( {"aaa字串陣列1","bbb字串陣列2"} );
console.log(ret);
console.pause();

Java 會在 CLASSPATH 指定的搜尋目錄下使用 Java 類名(替換'.'為'\')" 去查詢類,這類似 Windows 到 %PATH% 環境變數下搜尋檔案。CLASSPATH 已預設新增 "/java/"以及"/java/*.jar" 在首次建立 Java 虛擬機器器以前,可用下面的函數新增目錄到 CLASSPATH:

java.addClassPath("/java/"); 

以上引數指定目錄下的 *.jar 也會自動新增(不遞迴子目錄)。

也可以如下自記憶體或檔案直接載入類,注意下面的路徑前面的$操作符可將檔案編譯到程式碼中(釋出後不再需要原檔案)

HelloworldApp = jvm.import("aardio.sample.HelloworldApp"
     ,$"\java\aardio\sample\HelloworldApp.class");

以上檔案路徑前面的波浪線反斜槓(或斜杆)表示程式根目錄(開發時一般指工程目錄,釋出後指啟動EXE檔案所在的目錄)。

▶ Java 呼叫 aardio 函數

範例:

import console;
import java;  

//建立 Java 虛擬機器器
var jvm = java(); 

//匯入 Java 類
HelloworldApp = jvm.import( "aardio.sample.HelloworldApp");

//為 Java 提供 Native API
HelloworldApp.registerNative(
  function(env,jobject,jstring){
    var code = jvm.parseString(jstring) //也可以用 owner.parseString(jstring)
    var func = loadcode(code) 
    return jvm.string( func() )
  },"aardio","string(string)"
)

var helloworld = HelloworldApp();
console.log( "Java 函數返回值",helloworld.test_aardio() );

console.pause()

Java 類 HelloworldApp (在範例中可以找到)中如下宣告 aardio 函數:

static native String aardio(String code);

▶ aardio 直接執行 jar 程式

您僅需數句程式碼可以將 jar 匯入 aardio 生成軟體件。以 Java 編寫的軟體 FFDec 為例,原來只能自己去下載安裝 JRE,現在不用這麼做了,我用 aardio 寫了個例子:

import fsys.dlg;
import java.ffdec;

java.ffdec(
    fsys.dlg.open("*.swf|*.swf")
)

這個擴充套件庫 java.ffdec 的原始碼只有幾句:

import fsys;
import java;
import string.cmdline;

namespace java;

class ffdec { 
    ctor( ... ){
        var args = {
            "-Xmx1024m",
            "-Djna.nosys=true",
            "-Dsun.java2d.uiScale=1.0", 
            "-jar",jarPath 
        }
        
        ..table.append( args, ..string.cmdline.arguments(...))
    
        return ..java.popenProcess( args,{workDir=..io.fullpath( self.workDir : "/")} );  
    }
}

namespace ffdec{
    jarPath = ..io.appData("\aardio\std\java\ffdec\ffdec.jar");
    
    if(!..io.exist(path)){
        import sevenZip.lzma.tar;
        sevenZip.lzma.tar($"~\lib\java\ffdec\.res\ffdec.tar.lzma",..io.appData("aardio\std\java\") )
    } 
}

▶ aardio 呼叫 jar 的 main 函數

我們還可以直接呼叫 jar 的 main 函數來執行 Java 程式,以 YUICompressor 為例:

import java;
import java.yuiCompressor;
java.addClassPath("~\lib\java\yuiCompressor\.res\yuicompressor.jar")

var jvm = java();
YUICompressor = jvm.import( "com.yahoo.platform.yui.compressor.YUICompressor");
var ret = YUICompressor.main({"d:\test.js", "-o","d:\testmin.js"})

▶ Java 函數簽名

原本呼叫 Java 函數要寫簽名,但這個簽名寫起來是有些累的。所以 aardio 已經把這個事情完全給優化掉了,aardio 呼叫 Java 一般不用再去寫簽名。

但是 aardio 仍然允許以宣告普通靜態 API 函數的語法指定 Java 函數簽名 —— 然後由 aardio 自動翻譯為 Java 函數簽名。

舉個例子:

import java;
var jvm = java();

//匯入 Java 類
HelloworldApp = jvm.import( "aardio.sample.HelloworldApp");
  
//getMethod 用於獲取一個函數,引數 @2 指定函數簽名
var main = HelloworldApp.getMethod("main","string(string[])");

//通過宣告匯入的函數,如果引數是字串,可以把引數展開寫
var result = main("test1","test2")

但是上面的函數簽名一般可以省略,可以簡化為下面的程式碼:

HelloworldApp.main( {
  "aaa字串陣列1","bbb字串陣列2"
} )

上面的範例需要的 Java 類,可以在​ aardio 自帶範例裡找到: