当前位置: 首页 > 工具软件 > J2V8 > 使用案例 >

j2v8 js调用java_J2V8的使用

汝彭薄
2023-12-01

js 执行引擎说明

内核演变

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

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

v8 引擎是使用c++编写的,java要使用v8需要通过jni桥接,j2v8 就是起到这样的桥接作用

可用性+性能 => j2v8 > javaScrpitCore

J2v8 相对于 javaScriptCore 性能更优,具体优化了JNI的调用性能问题

J2v8对内存使用上的优化通过暴露api提供释放内存能力

内存管理

1手动管理的部分

j2v8 来说,以下对象必须手动释放:

自行创建的对象。例如 new V8Object() 创建的。

从 js 中主动获取的对象。例如 v8.getObject(xxx).

从 js 数组中提取的。例如 v8Array.getObject(0).

注意:

c++ 层作为参数传入到 java 的对象无需释放。因为它不是 java 自己创建的。

但是若传入的是数组,那么从数组中获取的对象必须释放,因为它是 java 主动获取的。

创建出的用作传给(或返回给) js 的对象必须释放,因为它是 java 创建的。

2 自动管理[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();

使用MemoryManager后

MemoryManager scope = new MemoryManager(v8); // 实例化 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)必须要统一线程使用(提供多线程的能力的同时保障多线程之间信息j2v8相关内容交互)

以下是AsyncTask的使用例子

override fun onPreExecute() {

super.onPreExecute()

if (v8.locker.hasLock()){

v8.locker.release() // 释放主线程的锁

}

}

override fun doInBackground(vararg params: int?): int {

v8.locker.acquire() // 子线程获得锁

// 执行一些 v8 操作

// ...

v8.locker.release() // 释放子线程的锁

return 1

}

override fun onPostExecute(result: int?) {

super.onPostExecute(result)

v8.locker.acquire() // 主线程重新获得锁

}

}

Note: 如果不需要使用多线程的能力,可让全局处在一个线程中常驻执行

//单线程池

private ExecutorService executorService = new ThreadPoolExecutor(

1,

1,

120,

TimeUnit.SECONDS,

new ArrayBlockingQueue(10));

public void commit(Runnable runnable){

executorService.execute(runnable);

}

private JSCoreManager(){

commit(new Runnable() {

@Override

public void run() {

//j2v8相关内容包装类对象创建【j2v8 runtime(v8)】

jsCore = new V8JSCore();

}

});

}

其他渲染引擎回调的jsbridge线程采用相同策略即可,也可以使用类AsyncTask中提到的方式在切换前程前release ,进入线程时acquire,线程执行完成后 release然后重新acquire 当前的线程

关于v8线程acquire和release的源码如下 [希望后期有v8 源码走读]

//创建全局的V8 runtime

V8 runtime = jsContext.getRuntime();

//jsContext 自行包装调用的 V8 静态方法

V8.createV8Runtime

public static V8 createV8Runtime(String globalAlias, String tempDirectory) {

if (!nativeLibraryLoaded) {

Object var2 = lock;

synchronized(lock) {

if (!nativeLibraryLoaded) {

load(tempDirectory);

}

}

}

//确认native库有加载(j2v8包装了对应os的native库加载)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 构造方法中会调用

this.locker = new V8Locker(this);

V8Locker(V8 runtime) {

this.runtime = runtime;

//这里和当前调用的线程绑定 之后执行对应runtime方法会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的成对切换即可

java 调用js

1.直接执行javascript

可直接使用 executeXXXScript 相关api执行js代码得到返回值

V8 runtime = V8.createV8Runtime(); // 创建 js 运行时

int result = runtime.executeIntegerScript("" // 执行一段 js 代码

+ "var hello = 'hello, ';\n"

+ "var world = 'world!';\n"

+ "hello.concat(world).length;\n");

System.out.println(result);

runtime.release(true); // 为 true 则会检查并抛出内存泄露错误(如果存在的话)便于及时发现

2.声明js函数名方式

a)定义js全局函数

function add(a, b){

return a + b

}

b) 方法调用执行

val arg = V8Array(v8).push(12).push(21) // 创建参数数组 arg为 V8Array[通过构造创建V8Array 根据前面规则最后需要手动释放]

val r = v8.executeIntegerFunction("add", arg) // 调用函数

arg.close() //别忘记释放对象

3.使用Function对象(v8Function)调用

使用场景:当js端传递给java 一个函数

if (v8.getType("add") == V8.V8_FUNCTION){ // 先判断 add 是不是一个函数

val arg = V8Array(v8).push(12).push(21)

val call = v8.getObject("add") as V8Function // 取得函数对象

val r = call.call(null, arg) // 调用它

arg.close()

call.close()

}

js调用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.使用注册接口方式

2.1 普通无返回值接口注册

private void registerNavigate() {

final JSCore jsCore = JSCoreManager.getInstance().getJSCore(1);

//对应有不同的参数返回值回调接口(这里是无参返回)

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

//注册 natives 方法 ,js调用该方法时 回调到nativesCallback 的 invoke 中

runtime.registerJavaMethod(nativesCallback, "natives");

}

2.2有参数据返回且带js返回callback到java端处理

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 不同写法对应的回调需要注意以下内容

1.js的方法写法不同,V8Object:receiver 不同

obj.func(params) 则这里的receiver是新建的这个func对应的V8Object

如果是通过func('methodName', params)写的,则receiver是全局的V8Object

2.对于V8Object可以通过keys方法查看对应挂载的元素 [在回调中测试挂载的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封装好的数据传递参数数组,获取array中的元素使用

getxxx(int index) [index对应参数位置, xxx为对应位置的数据类型]

使用非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) 方式注册

V8 对象和JSON转换

使用场景:js端向native端传递json object 时需要转换成String 【日志/json object 的数据解构】

public void log(final Object message) {

if (message instanceof String) {

System.out.println("[INFO] " + message);

} else {

//默认传过来的message 为 json Object 这里转换成json【通过JSON.stringify转换成js端传递过来的json object 为String】

V8 runtime = ((V8JSCore) JSCoreManager.getInstance().getJsCore()).getRuntime();

V8Object json = runtime.getObject("JSON");

V8Array args = new V8Array(runtime).push(message);

//result 为对应的jsonString

String result = (String) json.executeFunction("stringify", args);

Log.i("Console", "log(Console.java:22)" + result);

args.release();

json.release();

}

}

​```

//当前封装使用

```​

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

}

 类似资料: