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
- 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();
}