##C++调用js方法:##
我是按照v8给的sample中的process.cc中的实例做的,因为OpenGL绘制的工作在onDrawFrame()中执行的,所以我们要确保js中绘制代码的方法在onDrawFrame()中执行,所以我们需要在js中加上一些方法,通过v8来让C++决定方法什么时候调用。比如像下面的一个js方法,逻辑应该是这样的,我们通过调用initView(这个方法是通过v8包装的,关于如何包装的下面会详细介绍这里先说逻辑)来调用我们在C++中暴露给js的绘制接口,这个方法我们需要在onDrawFrame()(jni方法)中。function initView(){draw();}
有一个重要的问题就是关于v8初始化的时机,一开始我在Activity的onCreate()中单独用一个jni方法来初始化v8,其他关于v8的操作都是在onSurfaceChanged,onDrawFrame,onSurfaceCreated的jni方法中进行的,但是当我在onDrawFrame()的jni方法调用js方法initView()来绘制view的时候总是报错。在找不到原因后我把v8初始化的时机改成在onSurfaceCreated的jni方法中,由于v8只需初始化一次,所以要在清单文件中给游戏Activity的配置必须至少为android:configChanges="orientation|keyboardHidden|screenSize";改完之后方法调用成功。这里的原因可能是v8初始化与运行的环境问题,暂时不明。下面来看一下具体逻辑C++调用js方法v8的封装逻辑:
```java
v8Helper::Initialize();
Isolate::Scope isolate_scope(v8Helper::GetIsolate());
// Create a stack-allocated handle scope.
HandleScope handle_scope(v8Helper::GetIsolate());
// Create a new context.
Local<Context> context =creatTestContext(v8Helper::GetIsolate());
context_.Reset(v8Helper::GetIsolate(),context);
if (context.IsEmpty())
{
LOGI("Error creating context\n");
}
// Enter the context for compiling and running the hello world script.
Context::Scope context_scope(context);
// Create a string containing the JavaScript source code.
bool result = v8Helper::ExecuteString(context->GetIsolate(),
String::NewFromUtf8(context->GetIsolate(), jsSource,
NewStringType::kNormal).ToLocalChecked(),true, true);
LOGI("JS Script Execute Result :%d", result);
//调用js方法
Local<String> process_name =
String::NewFromUtf8(v8Helper::GetIsolate(), "initView", NewStringType::kNormal)
.ToLocalChecked();
Local<Value> process_val;
// If there is no Process function, or if it is not a function,
if (!context->Global()->Get(context, process_name).ToLocal(&process_val) ||
!process_val->IsFunction()) {
LOGI("initView is not a function\n");
}
// It is a function; cast it to a Function
Local<Function> process_fun = Local<Function>::Cast(process_val);
process_.Reset(v8Helper::GetIsolate(),process_fun);
```
```JavaScript
function initView(person){
}
```
不管是js调用C++的方法,还是C++调用js的方法,对这些方法的包装都应该是v8初始化完成后最先操作的。在 ``Local<Context> context =creatTestContext(v8Helper::GetIsolate()); ``之前的操作都是v8初始化常规代码我做了一些简单的封装,这个方法主要是来绑定C++暴露给js接口的所以暂时不讨论。注意 ``context_.Reset(GetIsolate(), context); ``这一句是用来保存当前上下文句柄的, ``process_.Reset(GetIsolate(), process_fun); ``是用来保存在js找出来的方法的句柄的,这两句是我们在以后的任何时候都可以调用js方法的关键。中间的一些代码都很常规,就是找出js中的方法名然后转成方法。应该注意到找出js方法的操作是在脚本加载并且执行完之后进行的,这是因为如果在加载js脚本之前找js的方法是肯定找不到的。context_是Global<Context>类型,process_是Global<Function>类型,这两个全局类型的对象其实是用来保存当前的上下文环境和需要以后来执行的方法的句柄的。以后我们我们可以通过这两个句柄,进入到相应上下文环境中执行相应的方法。接下来看一下,我们是怎么在C++中调用在js中找出来的这个方法的,因为我们需要在onDrawFrame()的jni方法中执行绘制代码所以在onDrawFrame()的jni方法中要这样来调用:
``` java
// Create a handle scope to keep the temporary object references.
HandleScope handle_scope(v8Helper::GetIsolate());
v8::Local<v8::Context> context =
v8::Local<v8::Context>::New(v8Helper::GetIsolate(), context_);
// Enter this processor's context so all the remaining operations
// take place there
Context::Scope context_scope(context);
// Set up an exception handler before calling the Process function
TryCatch try_catch(v8Helper::GetIsolate());
const int argc = 0;
Local<Value> argv[argc] = {};
v8::Local<v8::Function> process =
v8::Local<v8::Function>::New(v8Helper::GetIsolate(), process_);
Local<Value> result;
if (!process->Call(context, context->Global(), argc, argv).ToLocal(&result)) {
String::Utf8Value error(v8Helper::GetIsolate(), try_catch.Exception());
LOGI("call js function error:%s",*error);
}
```
首先是创建一栈区域来保存当前临时对象引用。
红框内的方法就是把之前保存在
我是按照v8给的sample中的process.cc中的实例做的,因为OpenGL绘制的工作在onDrawFrame()中执行的,所以我们要确保js中绘制代码的方法在onDrawFrame()中执行,所以我们需要在js中加上一些方法,通过v8来让C++决定方法什么时候调用。比如像下面的一个js方法,逻辑应该是这样的,我们通过调用initView(这个方法是通过v8包装的,关于如何包装的下面会详细介绍这里先说逻辑)来调用我们在C++中暴露给js的绘制接口,这个方法我们需要在onDrawFrame()(jni方法)中。function initView(){draw();}
有一个重要的问题就是关于v8初始化的时机,一开始我在Activity的onCreate()中单独用一个jni方法来初始化v8,其他关于v8的操作都是在onSurfaceChanged,onDrawFrame,onSurfaceCreated的jni方法中进行的,但是当我在onDrawFrame()的jni方法调用js方法initView()来绘制view的时候总是报错。在找不到原因后我把v8初始化的时机改成在onSurfaceCreated的jni方法中,由于v8只需初始化一次,所以要在清单文件中给游戏Activity的配置必须至少为android:configChanges="orientation|keyboardHidden|screenSize";改完之后方法调用成功。这里的原因可能是v8初始化与运行的环境问题,暂时不明。下面来看一下具体逻辑C++调用js方法v8的封装逻辑:
```java
v8Helper::Initialize();
Isolate::Scope isolate_scope(v8Helper::GetIsolate());
// Create a stack-allocated handle scope.
HandleScope handle_scope(v8Helper::GetIsolate());
// Create a new context.
Local<Context> context =creatTestContext(v8Helper::GetIsolate());
context_.Reset(v8Helper::GetIsolate(),context);
if (context.IsEmpty())
{
LOGI("Error creating context\n");
}
// Enter the context for compiling and running the hello world script.
Context::Scope context_scope(context);
// Create a string containing the JavaScript source code.
bool result = v8Helper::ExecuteString(context->GetIsolate(),
String::NewFromUtf8(context->GetIsolate(), jsSource,
NewStringType::kNormal).ToLocalChecked(),true, true);
LOGI("JS Script Execute Result :%d", result);
//调用js方法
Local<String> process_name =
String::NewFromUtf8(v8Helper::GetIsolate(), "initView", NewStringType::kNormal)
.ToLocalChecked();
Local<Value> process_val;
// If there is no Process function, or if it is not a function,
if (!context->Global()->Get(context, process_name).ToLocal(&process_val) ||
!process_val->IsFunction()) {
LOGI("initView is not a function\n");
}
// It is a function; cast it to a Function
Local<Function> process_fun = Local<Function>::Cast(process_val);
process_.Reset(v8Helper::GetIsolate(),process_fun);
```
```JavaScript
function initView(person){
}
```
不管是js调用C++的方法,还是C++调用js的方法,对这些方法的包装都应该是v8初始化完成后最先操作的。在 ``Local<Context> context =creatTestContext(v8Helper::GetIsolate()); ``之前的操作都是v8初始化常规代码我做了一些简单的封装,这个方法主要是来绑定C++暴露给js接口的所以暂时不讨论。注意 ``context_.Reset(GetIsolate(), context); ``这一句是用来保存当前上下文句柄的, ``process_.Reset(GetIsolate(), process_fun); ``是用来保存在js找出来的方法的句柄的,这两句是我们在以后的任何时候都可以调用js方法的关键。中间的一些代码都很常规,就是找出js中的方法名然后转成方法。应该注意到找出js方法的操作是在脚本加载并且执行完之后进行的,这是因为如果在加载js脚本之前找js的方法是肯定找不到的。context_是Global<Context>类型,process_是Global<Function>类型,这两个全局类型的对象其实是用来保存当前的上下文环境和需要以后来执行的方法的句柄的。以后我们我们可以通过这两个句柄,进入到相应上下文环境中执行相应的方法。接下来看一下,我们是怎么在C++中调用在js中找出来的这个方法的,因为我们需要在onDrawFrame()的jni方法中执行绘制代码所以在onDrawFrame()的jni方法中要这样来调用:
``` java
// Create a handle scope to keep the temporary object references.
HandleScope handle_scope(v8Helper::GetIsolate());
v8::Local<v8::Context> context =
v8::Local<v8::Context>::New(v8Helper::GetIsolate(), context_);
// Enter this processor's context so all the remaining operations
// take place there
Context::Scope context_scope(context);
// Set up an exception handler before calling the Process function
TryCatch try_catch(v8Helper::GetIsolate());
const int argc = 0;
Local<Value> argv[argc] = {};
v8::Local<v8::Function> process =
v8::Local<v8::Function>::New(v8Helper::GetIsolate(), process_);
Local<Value> result;
if (!process->Call(context, context->Global(), argc, argv).ToLocal(&result)) {
String::Utf8Value error(v8Helper::GetIsolate(), try_catch.Exception());
LOGI("call js function error:%s",*error);
}
```
首先是创建一栈区域来保存当前临时对象引用。
红框内的方法就是把之前保存在