建立一個跨平臺可複用C++代碼的實例工程(二)windows,android下webview中js調用原生代碼統一接口

三端界面統一用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;
}

詳細代碼可以點這裏下載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章