三端界面统一用webview加载h5实现,所以需要统一定义一个js调用原生代码的接口。其中windows方面比较好实现,用CefV8Handler,OnWebKitInitialized,OnProcessMessageReceived 等,轻松搞定。android需要转个弯,因为安卓没有原生的支持js异步调用原生代码的回调返回参数。要用evaluateJavascript来实现。所以js不好用匿名函数。只能在js端定义一个工具类。
js端的代码如下。用ddm类布置一个全局回调,原生代码调用这个回调后,qu'de函数名,关联的匿名回调函数,然后再分发回调。
<!DOCTYPE html>
<html>
<head>
<title>hello</title>
</head>
<body style="background-color:red;">
<button onclick="click_call()">click to call c++</button>
</body>
<script>
var ddm = {
cbs :{}
};
ddm.CallNative = function (func,obj){
var fobj = JSON.parse(func)
fobj.CallNativeCallBackEnabled = 1;
if(!obj){
fobj.CallNativeCallBackEnabled = 0;
}
var par0 = JSON.stringify(fobj);
if(fobj.CallNativeCallBackEnabled == 0){
return ddmcorejsapi.call(par0);
}
this.cbs[fobj.func] = obj;
return ddmcorejsapi.call(par0,this.CallNativeCallBack);
};
ddm.CallNativeCallBack = function(func,str){
alert("CallNativeCallBack " + func + " " + str);
ddm.cbs[func](str);
};
var data = {"b":"hello from js!"};
function click_call(){
alert("before call");
var that = this;
alert(ddm.CallNative('{"func":"test","param":{"param1":"test-45678","param2":0}}'));
alert(ddm.CallNative('{"func":"test","param":{"param1":"test-45678","param2":0}}',null));
alert(ddm.CallNative('{"func":"test","param":{"param1":"test-45678","param2":0}}',
function(a,b,c,d,e,f){
that.data.a = a;
alert("hello from callback " + JSON.stringify(that.data));
})
);
alert("after call");
}
</script>
<script>
//alert(ddmcorejsapi.call('{"func":"test","param":{"param1":"test-45678","param2":0}}',
// function(a,b,c,d,e,f){
// alert("hello from callback" + a);
// that.data.a = a;
// }));
//alert(ddmcorejsapi.call('{"func":"test","param":{"param1":"test-45678","param2":0}}'));
</script>
</html>
android端逻辑简单,插入webivew 实现
webView.addJavascriptInterface(new JSInterface(),"ddmcorejsapi");//添加js监听 这样html就能调用客户端
private final class JSInterface{
@SuppressLint("JavascriptInterface")
@JavascriptInterface
public String call(String msg){
try {
String [] ret = ddmcorejsapi.me().call(msg);
return ret[1];
}catch (Exception e1){
}
return "";
}
@SuppressLint("JavascriptInterface")
@JavascriptInterface
public String call(String msg,Object obj){
try {
JSONObject res = new JSONObject(msg);
final String msg0 = msg;
if(res.getInt("CallNativeCallBackEnabled") > 0) {
webView.post(new Runnable() {
@Override
public void run() {
String [] ret = ddmcorejsapi.me().call(msg0);
webView.evaluateJavascript("ddm.CallNativeCallBack('"+ret[0]+"','"+ret[1]+"')", null);
}
});
}
}catch (Exception e1){
}
return "{\"a\":\"from java\"}";
}
}
windows端比较麻烦了。
首先重载 OnWebKitInitialized 注册js工具
void SimpleApp::OnWebKitInitialized() {
CEF_REQUIRE_RENDERER_THREAD();
// Define the extension contents.
std::string extensionCode =
"var ddmcorejsapi;"
"if (!ddmcorejsapi)"
" ddmcorejsapi = {};"
"(function() {"
" ddmcorejsapi.call = function(jsonstr,func0) {"
" native function call(jsonstr,func0);"
" return call(jsonstr,func0);"
" };"
"})();";
// Create an instance of my CefV8Handler object.
//CefRefPtr<CefV8HandlerImpl> handler = new CefV8HandlerImpl();
// Register the extension.
CefRegisterExtension("v8/ddmcorejsapi", extensionCode, this);
}
然后v8里面处理js调用native代码的映射。
// in CefV8HandlerImpl.cpp
bool CefV8HandlerImpl::Execute(const CefString& name //JavaScript调用的C++方法名字
, CefRefPtr<CefV8Value> object //JavaScript调用者对象
, const CefV8ValueList& arguments //JavaScript传递的参数
, CefRefPtr<CefV8Value>& retval //需要返回给JavaScript的值设置给这个对象
, CefString& exception) { //通知异常信息给JavaScript
exception = "not implement function";
OutputDebugStringA(exception.ToString().c_str());
if (name == "registerJavascriptFunction") {
//保存JavaScript设置的回答函数,使得render进程持有这个callback函数
//以便js执行带有这个callback为参数的函数后,能够执行这个callback,详见步骤3
if (arguments.size() == 2 && arguments[0]->IsString() && arguments[1]->IsFunction()) {
std::string message_name = arguments[0]->GetStringValue();
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
int browser_id = context->GetBrowser()->GetIdentifier();
//message_name和browser_id是用于区分当js回调发生时,执行的是哪个js函数的回调
//cb_.insert(std::make_pair(browser_id,std::make_pair(context, arguments[1])));
// 此时可以发送一个信息给Browser进程,见第4个步骤
//registerJavascriptFunction有一个callback函数,
//因此render进程需要持有这个callback
exception = "";
return true;
}
else {
exception = "function arguments error!";
OutputDebugStringA(exception.ToString().c_str());
}
} else if (name == "call") {
//js调用sendMessage之后,c++处理SendMessage,这个没有回调函数,
//Execute函数return true,此次交互便结束。
//MessageBox(NULL, L"call", L"call", MB_OK);
OutputDebugStringA("call function....");
if (arguments.size() < 2) {
exception = "param missing";
OutputDebugStringA(exception.ToString().c_str());
return true;
}
if (!arguments[0]->IsString()) {
exception = "param type error";
OutputDebugStringA(exception.ToString().c_str());
return true;
}
/*
{
"func":"hello",
"param":{
"param1":"test",
"param2":0
.
.
.
}
}
*/
exception = "";
//OutputDebugStringA(funcname.ToString().c_str());
if (arguments.size() >= 2) { // 等于多于两个参数,只要前两个。第一个表示参数,第二个表示js回调函数。
CefRefPtr <CefV8Value> args1 = arguments[1];
CefString args0 = arguments[0]->GetStringValue();
if (args1 == nullptr || !args1->IsFunction()) { // 没有提供参数2,同步处理。
OutputDebugStringA("param2 is null or is not function");
OutputDebugStringA("2");
std::pair<CefString, CefString> out = ddmcorejsapi::me()->callapi(args0);// (funcname, param);
retval = CefV8Value::CreateString(out.second);
OutputDebugStringA(retval->GetStringValue().ToString().c_str());
}else {
OutputDebugStringA("there have callback function ! do it later");
retval = CefV8Value::CreateString("pending");
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
int browser_id = context->GetBrowser()->GetIdentifier();
int id = ddmcorejsapi::me()->getid();
//message_name和browser_id是用于区分当js回调发生时,执行的是哪个js函数的回调
cb_.insert(std::make_pair(std::make_pair(browser_id,id), std::make_pair(context, args1)));
CefRefPtr<CefProcessMessage> msg = CefProcessMessage::Create("ddmcorejsapi_req");
CefRefPtr<CefListValue> args = msg->GetArgumentList();
args->SetSize(3);
args->SetInt(0, browser_id);
args->SetInt(1, id);
args->SetString(2, args0);
//args->SetValue(2, args1);
// Send the process message to the browser process.
CefV8Context::GetCurrentContext()->GetBrowser()->SendProcessMessage(PID_BROWSER, msg);
}
}
exception = "";
return true;
}
return false;
}
封装一下数据处理好,原生c++代码回调js函数返回数据给js的接口
void CefV8HandlerImpl::CallJs(int bid, int id,CefString func, CefString ret, CefRefPtr<CefV8Context> cnt) {
CefV8ValueList args;
args.push_back(CefV8Value::CreateString(func));
args.push_back(CefV8Value::CreateString(ret));
CefRefPtr<CefV8Context> context = CefV8Context::GetCurrentContext();
CallbackMap::iterator iter = cb_.find(std::make_pair(bid, id));
if (iter != cb_.end()) {
iter->second.second->ExecuteFunctionWithContext(cnt, nullptr, args);
cb_.erase(iter);
}
}
#include "stdafx.h"
#include "ddmcorejsapi.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
ddmcorejsapi::ddmcorejsapi(){
}
ddmcorejsapi::~ddmcorejsapi(){
}
std::pair<CefString, CefString> ddmcorejsapi::callapi(const CefString& str) {
CefString func;
CefRefPtr<CefDictionaryValue> val;
std::pair<CefString, CefString> ret = std::make_pair("", "");
OutputDebugStringA("ddmcorejsapi::callapi");
//std::string str = param->GetStringValue();
CefRefPtr<CefValue> jsonObject = CefParseJSON(str, JSON_PARSER_ALLOW_TRAILING_COMMAS);
if (jsonObject == nullptr) {
OutputDebugStringA("1");
return ret;
}
CefRefPtr<CefDictionaryValue> dict = jsonObject->GetDictionary();
if (dict == nullptr) {
OutputDebugStringA("2");
return ret;
}
func = dict->GetString("func");
if (func == "") {
OutputDebugStringA("3");
return ret;
}
int cbe = dict->GetInt("CallNativeCallBackEnabled");
if (cbe < 0)
cbe = 0;
val = dict->GetDictionary("param");
if (val == nullptr) {
OutputDebugStringA("4");
return ret;
}
DDMCOREJSAPIFUNCMAP::iterator iter = mfuncs.find(func);
if (iter != mfuncs.end()) {
CefRefPtr<CefDictionaryValue> ret0 = (iter->second)(val);
CefRefPtr<CefValue> out0 = CefValue::Create();
out0->SetDictionary(ret0);
ret = std::make_pair(func,CefWriteJSON(out0, JSON_WRITER_DEFAULT));
}
return ret;
}
bool ddmcorejsapi::init() {
mid = 0;
mfuncs["test"] = Test;
return true;
}
ddmcorejsapi * ddmcorejsapi::me() {
static ddmcorejsapi* pThis = nullptr;
if (pThis == nullptr) {
pThis = new ddmcorejsapi();
if (pThis != nullptr) {
if (!pThis->init()) {
delete pThis;
pThis = nullptr;
}
}
}
return pThis;
}
CefRefPtr<CefDictionaryValue> ddmcorejsapi::Test(const CefRefPtr<CefDictionaryValue>& val) {
CefString valin = val->GetString("param1");
//CefString out = "{\"a\":";
CefRefPtr<CefDictionaryValue> out = CefDictionaryValue::Create();
out->SetString("ret", "OK");
out->SetString("paramin", valin);
return out;
}
详细代码可以点这里下载
网友评论