三端界面統一用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;
}
詳細代碼可以點這裏下載