Android 和 IOS 与webview的交互,Java端如何调用执行 客户端JS方法

  • Creator 版本:2.1

  • 目标平台:iOS / Android

项目中用到了webview登陆,登陆成功之后通知客户端,参照官方最新的2.1使用手册

cc.Class({
    extends: cc.Component,
    properties: {
        webview: cc.WebView
    },

    onLoad: function () {
        var scheme = "TestKey";// 这里是与内部页面约定的关键字
        function jsCallback (url) {
            // 这里的返回值是内部页面的 url 数值,
            // 需要自行解析自己需要的数据
            var str = url.replace(scheme + '://', '');
            var data = JSON.stringify(str);// {a: 0, b: 1}
        }

        this.webview.setJavascriptInterfaceScheme(scheme);
        this.webview.setOnJSCallback(jsCallback);
    }
});

// 因此当你需要通过内部页面交互 WebView 时,
// 应当设置内部页面 url 为:TestKey://(后面你想要回调到 WebView 的数据) 
// WebView 内部页面代码
<html>
<body>
    <dev>
        <input type="button" value="触发" onclick="onClick()"/>
    </dev>
</body>
<script>
    function onClick () {
        // 其中一个设置URL方案
        document.location = 'TestKey://{a: 0, b: 1}';
    }
</script>
</html>

结果一般都是 Failed to create URI from url


看了一下源码

try {
    URI uri = URI.create(urlString);
    if (uri != null && uri.getScheme().equals(mJSScheme)) {
        activity.runOnGLThread(new Runnable() {
            @Override
            public void run() {
                Cocos2dxWebViewHelper._onJsCallback(mViewTag, urlString);
            }
        });
        return true;
    }
} catch (Exception e) {
    Log.d(TAG, "Failed to create URI from url");
}

URI uri = URI.create(urlString);
这里就会进入catch里面了

尝试了不同的creator几个版本,只有某一个版本设置

var scheme = "http";

setJavascriptInterfaceScheme 和 setOnJSCallback 方法才能正确执行

尝试换一种思路,使用

Cocos2dxJavascriptJavaBridge.evalString("console.log('test')");

发现可以正确输出log,于是猜测这个函数可以执行全局的js函数,测试确实可行。下面是我的实现代码:

class Cocos2dxWebViewClient extends WebViewClient {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, final String urlString) {
            Cocos2dxActivity activity = (Cocos2dxActivity) getContext();

            try {
                /* ------------------------------登陆成功 start------------------------------- */
                String strType = urlString.substring(0, 6);
                if (strType.equals("peter:")) {
                    activity.runOnGLThread(new Runnable() {
                        @Override
                        public void run() {
                            String str = urlString.substring(6).substring(1);
                            str = str.substring(0, str.length() - 1);
                            Log.d(TAG, "peter str1 = " + str);
                            Cocos2dxJavascriptJavaBridge.evalString("login_callback(" + str + ")");
                        }
                    });
                    return false;
                }
                /* ------------------------------登陆成功 end--------------------------------- */

                URI uri = URI.create(urlString);
                if (uri != null && uri.getScheme().equals(mJSScheme)) {
                    activity.runOnGLThread(new Runnable() {
                        @Override
                        public void run() {
                            Cocos2dxWebViewHelper._onJsCallback(mViewTag, urlString);
                        }
                    });
                    return true;
                }
            } catch (Exception e) {
                Log.d(TAG, "Failed to create URI from url");
            }

            boolean[] result = new boolean[]{true};
            CountDownLatch latch = new CountDownLatch(1);

            // run worker on cocos thread
            activity.runOnGLThread(new ShouldStartLoadingWorker(latch, result, mViewTag, urlString));

            // wait for result from cocos thread
            try {
                latch.await();
            } catch (InterruptedException ex) {
                Log.d(TAG, "'shouldOverrideUrlLoading' failed");
            }

            return result[0];
        }

  • 前端代码:
let data = JSON.stringify({
	type:"login_success",
	phone: res.phone,
	headImg: res.headImg,
	account_type: res.accountType,
	isNative: res.isNative
});
if (!res.isNative) {
	top.postMessage(data, '*');
} else {	
	document.location = 'peter:' + data;
}

  • native平台js回调函数
    需要写在onload之类的函数里面,不然放在最外面,android会报错
window.login_callback = (para) => {
    console.log(JSON.stringify(para));
}

  • client端如何接收web端传递的值?
    上面判断非原声平台的if里面使用到了

top.postMessage(data, '*');

web端的client可以这么接收数值:

if (!cc.sys.isNative) {
    window.addEventListener("message",
        function (e) {
            if (e.data.target || typeof e.data != 'string') return;
            let data = JSON.parse(e.data);
            if (!data.type) return;
            switch (data.type) {
                case 'login_success': // 登陆成功,跳转
                    console.log(data);
                    break;
                
                default:
                    break;
            }
        }, false
    );
}

希望引擎团队优化一下文档里面的函数,api里面明明写了,就是调用不起来。@jare

3赞

同样遇到了这个问题,但这相当于自己写了一个监听啊,应该还有更好的办法,不知道大家是怎么解决的呢?