安卓原生与网页js的通信

18 篇文章 1 订阅
14 篇文章 0 订阅

前言

前一段时间在搞安卓的Hybird App,涉及到了安卓原生与网页js的通信交互。趁现在年末有空就做一下总结。

1、url schema

通过自定义协议来实现原生端与js端进行交互,自定义协议的格式如下:jsbridge://(method)?(params),即jsbridge://+方法名+参数。下面简单看一下这个url:jsbridge://showToast?message=text,showToast就是需要调用到的原生端方法,方法名就是showToast,参数则是message=text,key是message,value是text。这种协议类似于http协议,js端发出url请求,原生端进行拦截,判断是否是自定义请求协议,是则拦截下来,解析url的内容,然后进行相应的操作并返回数据。下面我们来看一下代码

<body>
    <input id="input" type="text" placeholder="请输入">
    <button id="btn">调用java方法</button>
</body>
<script>
    var input = document.getElementById('input')
    var btn = document.getElementById('btn')

    btn.addEventListener('click', function () {
        var text = input.value.trim();
        alert("jsbridge://showToast?message=" + text)
    })
</script>
private void initView() {
        webView = findViewById(R.id.webview);

        webView.getSettings().setJavaScriptEnabled(true);

        webView.setWebViewClient(new WebViewClient());

        webView.setWebChromeClient(new WebChromeClient(){
            @Override
            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                if(!message.startsWith("jsbridge://")){
                    return super.onJsAlert(view, url, message, result);
                }
                String[] split = message.split("=");
                showToast(split[1]);
                result.confirm();
                return true;
            }
        });
        webView.loadUrl("http://192.168.7.11:8080?time="+new Date().getTime());
    }

    public void showToast(String message){
        Toast.makeText(this,message,Toast.LENGTH_SHORT).show();
}

从上面可以看出来,我是通过js端自带window.alert来发出url请求的,在原生端中,我通过重写onJsAlert(WebView view, String url, String message, JsResult result) 这个方法来拦截window.alert所发出来的请求,然后通过判断message是否以jsbridge://开头,是则拦截,并实现自己的处理逻辑,不是则直接放行,默认处理。不过这种方式基本只会用在4.0以下的手机,而现在4.0以下的手机基本已经灭绝了,所以这种方式基本不会被用到。

2、往浏览器中注入对象

先上代码,在慢慢解释

<body>
    <div>jsbridge</div>
    <input id="input" type="text" placeholder="请输入">
    <button id="btn">调用java方法</button>
    <div id="msg"></div>
</body>
<script>
    var id = 1;
    var map = {}
    window.jsSdk = {
        showToast(message, fun) {
            if (fun) {
                var channelId = id++;
                map[channelId] = fun
            }
            window.android.showToast(message, channelId)
        },
        callBack(message, channelId) {
            if (map[channelId]) {
                map[channelId](message)
            }
        }
    }


    var input = document.getElementById('input')
    var btn = document.getElementById('btn')
    var msg = document.getElementById('msg')

    btn.addEventListener('click', function () {
        var text = input.value.trim();
        jsSdk.showToast("js调用java方法:" + text, function (message) {
            msg.innerHTML = message
        })
    })
</script>
private void initView() {
        webView = findViewById(R.id.webview);

        webView.getSettings().setJavaScriptEnabled(true);

        webView.setWebViewClient(new WebViewClient());

        webView.setWebChromeClient(new WebChromeClient());

        webView.addJavascriptInterface(new JsApi(),"android");

        webView.loadUrl("http://192.168.7.11:8080/jsbridge.html?time="+new Date().getTime());
    }
    private class JsApi{
        @JavascriptInterface
        public void showToast(String message, final int channelId){
            Toast.makeText(JsbridgeActivity.this,message+channelId,Toast.LENGTH_SHORT).show();
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    String jsCode = "javascript:jsSdk.callBack('调用成功',"+channelId+")";
                    webView.evaluateJavascript(jsCode,null);
                }
            });
        }
    }

首先看java代码,webView.addJavascriptInterface(new JsApi(),“android”)这个就是往webview中注入一个对象,对象名就叫做android,JsApi这类里面的方法就是提供给js端调用的方法,需要注意的是需要给js端调用方法中必须有@JavascriptInterface这个注解,否则会报错。然后再看看前端代码,window.android.showToast(message, channelId)就是调用原生端的方法,这里为什么要封装一个jsSdk?这是为了方便调用了java方法后获取调用后的结果。我在每次调用原生代码的时候,携带一个id参数,并用key-value的形式保存对应的回调方法,然后原生代码执行完后调用jsSdk中的callBack,把结果和id传递给callBack这个函数,callBack函数在根据id找出对应的回调函数,然后执行。

3、原生端调用js

<script>
    function showMessage(message) {
        alert(message)
        return 'success'
    }
</script>
public void javacalljs(View view){
        String jsCode = "javascript:showMessage('java调用js的代码')";
        webView.evaluateJavascript(jsCode, new ValueCallback<String>() {
            @Override
            public void onReceiveValue(String value) {
                Toast.makeText(UrlSchemaActivity.this,value,Toast.LENGTH_SHORT).show();
            }
        });
    }

从上面代码可以看见,js端的showMessage方法必须是挂载到window对象下的,否则原生端是调用不到的,在原生端中webView.evaluateJavascript就相当于js中的eval,不过webView.evaluateJavascript执行的脚本格式必须符合要求,就是 javascript:js代码,然后webView.evaluateJavascript得第二个参数是一个回调函数,如果原生端调用的是js端的方法,并且该方法有返回值,则可以使用该回调函数接收返回的值。

总结

最后,其实有很多的第三方库已经给我们封装好了原生端与js端的通信桥梁,比如JsBridge和DSBridge-Android,这些用起来都比较的舒服。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值