原文: JDK中內嵌JS引擎介紹及使用 - Stars-One的雜貨小窩
最近研究閱讀這個APP,其主要功能就是通過一個個書源,從而實現行動端閱讀的體驗
比如說某些線上小說閱讀網站,會加上相應的廣告,從而影響使用者閱讀體驗,於是閱讀這個APP就是做了類似淨化閱讀體驗
但是小說閱讀網站千千萬萬,如果去適配每個小說閱讀網站,豈不是累死,且作者也會有被髮律師函的危險,於是作者提供了對應的工具,允許各位使用者可自定義書源,閱讀APP則通過匯入書源,即可實現對某個小說閱讀網站的支援
這裡說的書源,實際上就是JS指令碼程式碼,作者本質上是使用了rhino這個Java的JS引擎技術來實現的
JDK1.6和JDK1.7採用Rhino。Nashorn 支援 ECMAScript 5.1 規範,使用基於 JSR 292 的新語言特性,其中包含在 JDK 7 中引入的 invokedynamic,將 JavaScript 編譯成 Java 位元組碼。
從JDK1.8開始,Java採用Nashorn作為嵌入式 JavaScript 引擎。
本質上,都是通用標準ECMAScriptJS規範,沒啥過多的區別
JDK內建的方式,其實也就是將對應JS引擎的jar包一起整合在Java環境裡了,如果想使用新版本的JS引擎,可以去對應的JS引擎上找最新版本的jar包或通過maven來參照依賴即可
rhino目前是由火狐瀏覽器團隊開發的,使用Java寫的一個JS引擎,目前也是在更新,如果想要更新,而不想更新JDK的話,可以直接使用maven引入最新的jar包即可
Nashorn之前是oracle團隊在開發,現在看github的話,是有openjdk團隊在維護
而在Android平臺方面,由於平臺對JVM進行了調整,所以預設是不支援的,但是好訊息的是,有大神將移植到了Android平臺上,具體可以檢視APISENSE/rhino-android,需要的話可以直接引入依賴即可
不過作者寫的使用檔案不太清晰,這裡我比較推薦使用閱讀APP開發者的gedoor/rhino-android,實際上也是基於APISENSE/rhino-android
進行了一定的調整,使用起來和Java內建的步驟是一樣的
可能有同學就有疑惑了,使用這個JS引擎能有什麼作用呢?
這裡就舉個例子,有個加密方法,是通過js去實現的,但是現在如果讓我們想要獲取到加密的結果,得通過閱讀JS原始碼,看懂加密思路後再使用Java程式碼重現實現,是不是十分的複雜?
但如果採用JS引擎,我們只需要將對應的JS方法程式碼拿到,之後我們只需要傳引數,通過JS引擎執行加密過程,即可得到加密後的引數了
PS: 最後,注意一下,上述說到的兩種JS引擎,只支援部分ES6特性,所有,如果你的JS程式碼有ES6特性的,可能執行的時候會報錯!
下文以JDK8內建的JS引擎為例,講解一下使用
首先,我們需要通過ScriptEngineManager
物件獲取JS指令碼引擎engine物件,之後通過engine.eval()
方法來執行我們需要的js程式碼
// 1、獲得指令碼引擎物件,選擇指令碼語言
val manager = ScriptEngineManager()
// 亦可以是js縮寫,代表JavaScript指令碼語言
val engine = manager.getEngineByName("js")
val result = engine.eval("""
var num = 5+2;
num
""".trimIndent())
println(result)
PS: 如果是
gedoor/rhino-android
,getEngineByName()
方法裡面傳rhino
這個字串!
比較多的情況就是,我們需要通過JS去執行邏輯,之後得到返回的結果,有以下2種方式獲取數值
作用域變數的方式程式碼如下:
// 1、獲得指令碼引擎物件,選擇指令碼語言
val manager = ScriptEngineManager()
// 亦可以是js縮寫,代表JavaScript指令碼語言
val engine = manager.getEngineByName("js")
//宣告一個變數
engine.put("finalResult","")
//註冊一個js方法
engine.eval("""
function add(a,b){
return a + b
}
""".trimIndent())
//執行獲取結果,賦予finalResult資料
engine.eval("finalResult = add(2,3)")
println(engine["finalResult"])
或者直接在js中宣告變數finalResult
,如下程式碼:
// 1、獲得指令碼引擎物件,選擇指令碼語言
val manager = ScriptEngineManager()
// 亦可以是js縮寫,代表JavaScript指令碼語言
val engine = manager.getEngineByName("js")
//註冊一個js方法
engine.eval("""
function add(a,b){
return a + b
}
""".trimIndent())
//執行獲取結果
engine.eval("var finalResult = add(2,3)")
println(engine["finalResult"])
// 1、獲得指令碼引擎物件,選擇指令碼語言
val manager = ScriptEngineManager()
// 亦可以是js縮寫,代表JavaScript指令碼語言
val engine = manager.getEngineByName("js")
val file = File("D:\\temp\\qrcode.gif")
//將檔案的物件設定為JS變數
engine.put("myFile",file)
//註冊一個js方法
engine.eval("""
print(myFile.getPath());
""".trimIndent())
上面js中的print實際上也是Java提供的方法,而myFile則是我們Java中的一個File物件,JS程式碼中可以使用這個物件及相應的Java方法