前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >建立一个跨平台可复用C++代码的实例工程(二)windows,android下webview中js调用原生代码统一接口

建立一个跨平台可复用C++代码的实例工程(二)windows,android下webview中js调用原生代码统一接口

作者头像
xiny120
发布2019-07-09 17:59:21
1K0
发布2019-07-09 17:59:21
举报
文章被收录于专栏:毛毛v5毛毛v5

三端界面统一用webview加载h5实现,所以需要统一定义一个js调用原生代码的接口。其中windows方面比较好实现,用CefV8Handler,OnWebKitInitialized,OnProcessMessageReceived 等,轻松搞定。android需要转个弯,因为安卓没有原生的支持js异步调用原生代码的回调返回参数。要用evaluateJavascript来实现。所以js不好用匿名函数。只能在js端定义一个工具类。 js端的代码如下。用ddm类布置一个全局回调,原生代码调用这个回调后,qu'de函数名,关联的匿名回调函数,然后再分发回调。

代码语言:javascript
复制
<!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 实现

代码语言:javascript
复制
webView.addJavascriptInterface(new JSInterface(),"ddmcorejsapi");//添加js监听 这样html就能调用客户端
代码语言:javascript
复制
    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工具

代码语言:javascript
复制
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代码的映射。

代码语言:javascript
复制
// 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的接口

代码语言:javascript
复制
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);
    }
}
代码语言:javascript
复制
#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;
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.07.05 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档