J2V8的使用

js 執(zhí)行引擎說(shuō)明

瀏覽器發(fā)展歷史

內(nèi)核演變

Gecko(Netscape) - Trident(IE) - Gecko(firefox Mozilla) - Safari(webkit) - Chrome(Chromium) - Chrome(blink)

javascript引擎 / 什么是J2V8/為什么要使用J2V8

js引擎在android端的使用是怎樣的體驗(yàn)?

v8 引擎是使用c++編寫(xiě)的,java要使用v8需要通過(guò)jni橋接,j2v8 就是起到這樣的橋接作用

可用性+性能 => j2v8 > javaScrpitCore

J2v8 相對(duì)于 javaScriptCore 性能更優(yōu),具體優(yōu)化了JNI的調(diào)用性能問(wèn)題

J2v8對(duì)內(nèi)存使用上的優(yōu)化通過(guò)暴露api提供釋放內(nèi)存能力

內(nèi)存管理

1手動(dòng)管理的部分

j2v8 來(lái)說(shuō),以下對(duì)象必須手動(dòng)釋放:
自行創(chuàng)建的對(duì)象。例如 new V8Object() 創(chuàng)建的。
從 js 中主動(dòng)獲取的對(duì)象。例如 v8.getObject(xxx).
從 js 數(shù)組中提取的。例如 v8Array.getObject(0).
注意:
c++ 層作為參數(shù)傳入到 java 的對(duì)象無(wú)需釋放。因?yàn)樗皇?java 自己創(chuàng)建的。
但是若傳入的是數(shù)組,那么從數(shù)組中獲取的對(duì)象必須釋放,因?yàn)樗?java 主動(dòng)獲取的。
創(chuàng)建出的用作傳給(或返回給) js 的對(duì)象必須釋放,因?yàn)樗?java 創(chuàng)建的。

2 自動(dòng)管理[MemoryManager]

使用MemoryManager 前

loDash = nodeJS.require(new File("/Users/irbull/node_modules/lodash"));
?
V8Object o1 = o("a", 1);
V8Object o2 = o("b", 2);
V8Object o3 = o("c", 3);
V8Object objects = (V8Object) loDash.executeJSFunction("assign", o1, o2, o3);
LoDashObject e1 = loDash(objects);
LoDashObject e2 = e1.e("values");
V8Function f = f((V8Object receiver, V8Array parameters) -> parameters.getInteger(0) * 3);
LoDashObject result = e2.e("map",f);
System.out.println(result);
?
loDash.release();
e1.release();
e2.release();
f.release();
o1.release();
o2.release();
o3.release();
result.release();
objects.release();</pre>

使用MemoryManager后

MemoryManager scope = new MemoryManager(v8); // 實(shí)例化 MemoryManager
loDash = nodeJS.require(new File("/Users/irbull/node_modules/lodash"));
?
V8Object objects = (V8Object) loDash.executeJSFunction("assign", o("a", 1), o("b", 2), o("c", 3));
LoDashObject result = loDash(objects).e("values").e("map",
 f((V8Object receiver, V8Array parameters) -> parameters.getInteger(0) * 3));
System.out.println(result);
?
scope.release(); // 釋放

多線程

j2v8中使用runtime(v8 createV8Runtime)必須要統(tǒng)一線程使用(提供多線程的能力的同時(shí)保障多線程之間信息j2v8相關(guān)內(nèi)容交互)

以下是AsyncTask的使用例子

 override fun onPreExecute() {
 super.onPreExecute()
 if (v8.locker.hasLock()){
 v8.locker.release() // 釋放主線程的鎖
 }
 }
 override fun doInBackground(vararg params: int?): int {
 v8.locker.acquire() // 子線程獲得鎖
 // 執(zhí)行一些 v8 操作
 // ...
 v8.locker.release() // 釋放子線程的鎖
 return 1
 }
 override fun onPostExecute(result: int?) {
 super.onPostExecute(result)
 v8.locker.acquire() // 主線程重新獲得鎖
 }
}

Note: 如果不需要使用多線程的能力,可讓全局處在一個(gè)線程中常駐執(zhí)行

 //單線程池
 private ExecutorService executorService = new ThreadPoolExecutor(
 1,
 1,
 120,
 TimeUnit.SECONDS,
 new ArrayBlockingQueue<Runnable>(10));
?
public void commit(Runnable runnable){
 executorService.execute(runnable);
 }
?
 private JSCoreManager(){
 commit(new Runnable() {
 @Override
 public void run() {
 //j2v8相關(guān)內(nèi)容包裝類(lèi)對(duì)象創(chuàng)建【j2v8 runtime(v8)】
 jsCore = new V8JSCore();
 }
 });
 }

其他渲染引擎回調(diào)的jsbridge線程采用相同策略即可,也可以使用類(lèi)AsyncTask中提到的方式在切換前程前release ,進(jìn)入線程時(shí)acquire,線程執(zhí)行完成后 release然后重新acquire 當(dāng)前的線程

關(guān)于v8線程acquire和release的源碼如下 [希望后期有v8 源碼走讀]

//創(chuàng)建全局的V8 runtime 
V8 runtime = jsContext.getRuntime(); 
//jsContext 自行包裝調(diào)用的 V8 靜態(tài)方法
V8.createV8Runtime
?
public static V8 createV8Runtime(String globalAlias, String tempDirectory) {
 if (!nativeLibraryLoaded) {
 Object var2 = lock;
 synchronized(lock) {
 if (!nativeLibraryLoaded) {
 load(tempDirectory);
 }
 }
 }
 //確認(rèn)native庫(kù)有加載(j2v8包裝了對(duì)應(yīng)os的native庫(kù)加載)com.eclipsesource.v8.LibraryLoader
 checkNativeLibraryLoaded();
 if (!initialized) {
 _setFlags(v8Flags);
 initialized = true;
 }
 //entrance to v8 reference
 V8 runtime = new V8(globalAlias);
 Object var3 = lock;
 synchronized(lock) {
 ++runtimeCounter;
 return runtime;
 }
 }

?
// V8 構(gòu)造方法中會(huì)調(diào)用
 this.locker = new V8Locker(this);
?
 V8Locker(V8 runtime) {
 this.runtime = runtime;
 //這里和當(dāng)前調(diào)用的線程綁定 之后執(zhí)行對(duì)應(yīng)runtime方法會(huì)checkThread
 this.acquire();
 }
?
 public void checkThread() {
 if (this.released && this.thread == null) {
 throw new Error("Invalid V8 thread access: the locker has been released!");
 } else if (this.thread != Thread.currentThread()) {
 throw new Error("Invalid V8 thread access: current thread is " + Thread.currentThread() + " while the locker has thread " + this.thread);
 }
 }
?
//如何在不同線程中切換參考AsyncTask中和以上描述做release 和 acquire的成對(duì)切換即可

java 調(diào)用js

1.直接執(zhí)行javascript

可直接使用 executeXXXScript 相關(guān)api執(zhí)行js代碼得到返回值

 V8 runtime = V8.createV8Runtime(); // 創(chuàng)建 js 運(yùn)行時(shí)
 int result = runtime.executeIntegerScript("" // 執(zhí)行一段 js 代碼
 + "var hello = 'hello, ';\n"
 + "var world = 'world!';\n"
 + "hello.concat(world).length;\n");
 System.out.println(result);
 runtime.release(true); // 為 true 則會(huì)檢查并拋出內(nèi)存泄露錯(cuò)誤(如果存在的話)便于及時(shí)發(fā)現(xiàn)

2.聲明js函數(shù)名方式

a)定義js全局函數(shù)

function add(a, b){
 return a + b
}

b) 方法調(diào)用執(zhí)行

val arg = V8Array(v8).push(12).push(21) // 創(chuàng)建參數(shù)數(shù)組 arg為 V8Array[通過(guò)構(gòu)造創(chuàng)建V8Array 根據(jù)前面規(guī)則最后需要手動(dòng)釋放]
val r = v8.executeIntegerFunction("add", arg) // 調(diào)用函數(shù)
arg.close() //別忘記釋放對(duì)象

3.使用Function對(duì)象(v8Function)調(diào)用

使用場(chǎng)景:當(dāng)js端傳遞給java 一個(gè)函數(shù)

if (v8.getType("add") == V8.V8_FUNCTION){ // 先判斷 add 是不是一個(gè)函數(shù)
 val arg = V8Array(v8).push(12).push(21)
 val call = v8.getObject("add") as V8Function // 取得函數(shù)對(duì)象
 val r = call.call(null, arg) // 調(diào)用它
 arg.close()
 call.close()
}

js調(diào)用java

1.使用反射方式

public class Console {
?
 public void log(final Object message) {
 System.out.println("[INFO] " + message);
 }
?
 public void err(final Object message) {
 System.out.println("[ERROR] " + message);
 }
?
}  
?
private void registerConsoleLog() {
 //反射注入
 Console console = new Console();
 V8Object v8Console = new V8Object(runtime);
 runtime.add("console", v8Console);
 v8Console.registerJavaMethod(console, "log", "log", new Class<?>[]{Object.class});
 v8Console.registerJavaMethod(console, "err", "err", new Class<?>[]{Object.class});
 }

2.使用注冊(cè)接口方式

2.1 普通無(wú)返回值接口注冊(cè)
private void registerNavigate() {
 final JSCore jsCore = JSCoreManager.getInstance().getJSCore(1);
 //對(duì)應(yīng)有不同的參數(shù)返回值回調(diào)接口(這里是無(wú)參返回)
 JavaVoidCallback nativesCallback = new JavaVoidCallback() {
 @Override
 public void invoke(V8Object receiver, V8Array parameters) {
 ThreadUtils.checkThread(((V8JSCore)jsCore).getRuntime(), "invoke navigate");
 if (parameters.length() > 0) {
 String method = (String) parameters.get(0);
 V8Array pathArr = (V8Array) parameters.get(1);
 String url = (String) pathArr.get(0);
 if (TextUtils.equals(method, "navigateTo")) {
 url =  "file:///storage/emulated/0/Download/mock/5d6f2af33d5e877599fdb12c/h5/index.html";
 final String finalUrl = url;
 ((V8JSCore)jsCore).getMainHandler().post(new Runnable() {
 @Override
 public void run() {
 mPageManager.navigateTo(finalUrl);
 }
 });
 }
 }
 }
 };
 V8 runtime = ((V8JSCore)jsCore).getRuntime();
 //注冊(cè) natives  方法 ,js調(diào)用該方法時(shí) 回調(diào)到nativesCallback 的 invoke 中
 runtime.registerJavaMethod(nativesCallback, "natives");
 }
2.2有參數(shù)據(jù)返回且?guī)s返回callback到j(luò)ava端處理
private void testCallback() {
 JavaCallback callback = new JavaCallback() {
 @Override
 public Object invoke(V8Object receiver, V8Array parameters) {
 String value = "";
 String[] keys = receiver.getKeys();
 for (int i = 0; i < keys.length; i++) {
 Log.e("V8JSCore", "invoke(V8JSCore.java:245)" + keys[i]);
 }
//                String method = (String) parameters.get(0);
//                if (TextUtils.equals(method, "call")) {
 String first = receiver.getString("first");
 V8Function function = (V8Function) parameters.get(0);
 V8Array array = new V8Array(runtime).push("kiwi");
 Object call = function.call(null, array);
 Log.e("V8JSCore", "invoke(V8JSCore.java:241)" + first);
 value = first + "count";
//                }
 return value;
 }
 };
 runtime.registerJavaMethod(callback, "print");
//        natives('navigateTo', [url], cb);
 String str = "var array1 = [{first:'Ian'}, {first:'Jordi'}, {first:'Holger'}];\n" +
 "var cb = function(data, a, b) {" +
 " console.log(data);\n" +
 "};\n" +
 "        for ( var i = 0; i < array1.length; i++ ) {\n" +
 "            var result = print.call(array1[i], cb);\n" +
 //   "            var result = print.call(array1[i], array1[i], array1[i], cb);\n" +
 "            console.log(result);\n" +
 "        }";
 JSValue jsValue = evaluateScript(str);
 Log.e("V8JSCore", "testCallback(V8JSCore.java:259)" + jsValue.toString());
 }
2.3 不同寫(xiě)法對(duì)應(yīng)的回調(diào)需要注意以下內(nèi)容

1.js的方法寫(xiě)法不同,V8Object:receiver 不同
obj.func(params) 則這里的receiver是新建的這個(gè)func對(duì)應(yīng)的V8Object
如果是通過(guò)func('methodName', params)寫(xiě)的,則receiver是全局的V8Object

2.對(duì)于V8Object可以通過(guò)keys方法查看對(duì)應(yīng)掛載的元素 [在回調(diào)中測(cè)試掛載的key 如下]

String[] keys = receiver.getKeys();
 invoke(V8JSCore.java:245)global
09-18 14:49:30.210 15938-15969/app.java.myapplication E/V8JSCore: invoke(V8JSCore.java:245)log
09-18 14:49:30.210 15938-15969/app.java.myapplication E/V8JSCore: invoke(V8JSCore.java:245)console
09-18 14:49:30.210 15938-15969/app.java.myapplication E/V8JSCore: invoke(V8JSCore.java:245)natives
09-18 14:49:30.210 15938-15969/app.java.myapplication E/V8JSCore: invoke(V8JSCore.java:245)YmGlobal
09-18 14:49:30.210 15938-15969/app.java.myapplication E/V8JSCore: invoke(V8JSCore.java:245)request
09-18 14:49:30.210 15938-15969/app.java.myapplication E/V8JSCore: invoke(V8JSCore.java:245)__GLOBAL_DOCUMENT_CACHE@4
09-18 14:49:30.210 15938-15969/app.java.myapplication E/V8JSCore: invoke(V8JSCore.java:245)__INDIVIDUAL_ONE_VERSION_ev-store_ENFORCE_SINGLETON
09-18 14:49:30.210 15938-15969/app.java.myapplication E/V8JSCore: invoke(V8JSCore.java:245)__INDIVIDUAL_ONE_VERSION_ev-store
09-18 14:49:30.210 15938-15969/app.java.myapplication E/V8JSCore: invoke(V8JSCore.java:245)Base64
09-18 14:49:30.210 15938-15969/app.java.myapplication E/V8JSCore: invoke(V8JSCore.java:245)YmCore
09-18 14:49:30.210 15938-15969/app.java.myapplication E/V8JSCore: invoke(V8JSCore.java:245)Page
09-18 14:49:30.210 15938-15969/app.java.myapplication E/V8JSCore: invoke(V8JSCore.java:245)print
09-18 14:49:30.210 15938-15969/app.java.myapplication E/V8JSCore: invoke(V8JSCore.java:245)array1
09-18 14:49:30.210 15938-15969/app.java.myapplication E/V8JSCore: invoke(V8JSCore.java:245)cb
09-18 14:49:30.210 15938-15969/app.java.myapplication E/V8JSCore: invoke(V8JSCore.java:245)i
09-18 14:49:30.210 15938-15969/app.java.myapplication E/V8JSCore: invoke(V8JSCore.java:245)result
  1. parameters 為 V8Array,是j2v8封裝好的數(shù)據(jù)傳遞參數(shù)數(shù)組,獲取array中的元素使用
    getxxx(int index) [index對(duì)應(yīng)參數(shù)位置, xxx為對(duì)應(yīng)位置的數(shù)據(jù)類(lèi)型]
    使用非function方式
val callback = V8Function(v8,
 { receiver: V8Object, parameters: V8Array -> System.out.println(parameters.getInteger(0)) })
val arg = V8Array(v8).push(1).push(2).push(callback)
v8.executeVoidFunction("add", arg)
//也可使用v8.add("print", callback) 方式注冊(cè)

V8 對(duì)象和JSON轉(zhuǎn)換

使用場(chǎng)景:js端向native端傳遞json object 時(shí)需要轉(zhuǎn)換成String 【日志/json object 的數(shù)據(jù)解構(gòu)】

public void log(final Object message) {
 if (message instanceof String) {
 System.out.println("[INFO] " + message);
 } else {
 //默認(rèn)傳過(guò)來(lái)的message 為 json Object 這里轉(zhuǎn)換成json【通過(guò)JSON.stringify轉(zhuǎn)換成js端傳遞過(guò)來(lái)的json object 為String】
 V8 runtime = ((V8JSCore) JSCoreManager.getInstance().getJsCore()).getRuntime();
 V8Object json = runtime.getObject("JSON");
 V8Array args = new V8Array(runtime).push(message);
 //result 為對(duì)應(yīng)的jsonString
 String result = (String) json.executeFunction("stringify", args);
 Log.i("Console", "log(Console.java:22)" + result);
 args.release();
 json.release();
 }
 }
?```

//當(dāng)前封裝使用
```?
 public String v8ToJSON(Object v8Obj) {
 if (v8Obj == null) {
 return "";
 }
 if (v8Obj instanceof V8Object) {
 V8 runtime = ((V8JSCore) JSCoreManager.getInstance().getJsCore()).getRuntime();
 V8Object json = runtime.getObject("JSON");
 V8Array args = new V8Array(runtime).push(v8Obj);
 String result = (String) json.executeFunction("stringify", args);
 Log.i("Console", "log(Console.java:22)" + result);
 args.release();
 json.release();
 return result;
 }
 return v8Obj.toString();
 }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容