Android之旅

Android WebView和Js交互

2019-03-31  本文已影响4人  h2coder

现在纯原生Android开发越来越少了,现在一般App都会混合开发,其他混合的技术先不说,最常用就是WebView加载H5页面,再App客户端和Web端交互,提供一些用户信息、客户端Api等,本篇介绍WebView调用Js,Js调用Android方法的知识。

img.png

本文使用的Html文件

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>WebView</title>
    <style type="text/css">
        body {
            background: #caff0c;
        }

        .btn {
            line-height: 40px;
            margin: 10px;
            background: #cccccc;
        }

        #input {
            margin-left: 5px;
        }
    </style>
</head>
<body>
<h2>WebView</h2>
<div><span>请输入要传递的数据</span><input type="text" id="input"></div>
<div id="btn"><span class="btn">点我</span></div>
<div><a href="info://info?name=wally&age=18">点我跳转链接</a></div>

<script type="text/javascript">
    //5.按钮点击,调用客户端方法
    var btnEle = document.getElementById("btn");
    var inputEle = document.getElementById("input");
    btnEle.addEventListener("click", function () {
        var str = inputEle.value;
        if (window.androidJs) {
            window.androidJs.setValue(str);
        } else {
            alert("window androidJs is not found!")
        }
    })

    //提供给客户端调用的有参Js方法,设置输入文字
    var remote = function (str) {
        inputEle.value = str;
    }

    //带返回值的Js方法,给客户端获得输入的文字
    var remoteWithValue = function () {
        return inputEle.value;
    }
</script>
</body>
</html>

JS调用Android方法

Android客户端,设置允许加载Js代码,提供Js接口类,注入到Web端的Window中。

/**
 * 加载的Html位置
 */
private static final String SETUP_HTML = "file:///android_asset/index.html";

//1.允许WebView加载Js代码
mWebView.getSettings().setJavaScriptEnabled(true);

/**
 * 2.编写一个提供给Web的Js接口类
 */
public class JsInterface {
    private static final String TAG = JsInterface.class.getSimpleName();
    private Callback mCallback;

    public JsInterface(Callback callback) {
        mCallback = callback;
    }

    @JavascriptInterface
    public void setValue(String value) {
        if (TextUtils.isEmpty(value)) {
            return;
        }
        Log.d(TAG, "value: " + value);
        if (mCallback != null) {
            mCallback.onSetValue(value);
        }
    }

    public interface Callback {
        void onSetValue(String value);
    }
}

//3.给WebView添加Js接口
mWebView.addJavascriptInterface(new JsInterface(new JsInterface.Callback() {
    @Override
    public void onSetValue(final String value) {
        mMainHandler.post(new Runnable() {
            @Override
            public void run() {
                mResult.setText("value: " + value);
            }
        });
    }
}), "androidJs");
        
//4.加载Html
mWebView.loadUrl(SETUP_HTML);

Html中按钮点击时,Js调用客户端方法

//5.按钮点击,调用客户端方法
var btnEle = document.getElementById("btn");
var inputEle = document.getElementById("input");
btnEle.addEventListener("click", function () {
    var str = inputEle.value;
    //判断是否在客户端环境
    if (window.androidJs) {
        window.androidJs.setValue(str);
    } else {
        alert("window androidJs is not found!")
    }
})
//和前端协商的协议前缀
private static final String CALLBACK_SCHEME = "info://info";

mWebView.setWebViewClient(new WebViewClient() {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        //是否拦截Url
        boolean isIntercept = url.startsWith(CALLBACK_SCHEME);
        //处理拦截特定前缀Url
        handleUrlIntercept(url);
        if (isIntercept) {
            return true;
        } else {
            return super.shouldOverrideUrlLoading(view, url);
        }
    }
});
        
/**
 * 处理拦截特定前缀Url
 */
private void handleUrlIntercept(String text) {
    HashMap<String, String> paramsMap = new HashMap<>(5);
    String result = text.replaceFirst(CALLBACK_SCHEME.concat("\\?"), "");
    String[] paramsGroup = result.split("&");
    //按组拆分bold=false
    for (String groupStr : paramsGroup) {
        String[] paramsKeyValue = groupStr.split("=");
        //参数和值拆分
        String paramsName = paramsKeyValue[0];
        String paramsValue = paramsKeyValue[1];
        paramsMap.put(paramsName, paramsValue);
    }
    final String name = paramsMap.get("name");
    final String age = paramsMap.get("age");
    toast("拦截Url,获取参数 -> " + "name:" + name + " age:" + age);
}

Web端,超链接加载自定义协议,客户端进行拦截

//点击超链接,加载自定义协议,客户端进行拦截
<div><a href="info://info?name=wally&age=18">点我跳转链接</a></div>
mWebView.setWebChromeClient(new WebChromeClient() {
    @Override
    public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
        //拦截掉,回调4.4一下的Js返回值处理
        OnMethodResultCallback callback = mCompatKitkatJsMethodCallbackMap.get(url);
        if (callback != null) {
            callback.onMethodResult(message);
            mCompatKitkatJsMethodCallbackMap.remove(url);
            //拦截后必须取消掉,不发送到客户端WebView,不调用也不行,会阻塞等待结果
            result.cancel();
            return true;
        } else {
            return super.onJsPrompt(view, url, message, defaultValue, result);
        }
    }
});

Android调用Js方法

/**
 * 调用JS方法获取返回值的监听
 */
interface OnMethodResultCallback {
    /**
     * 返回值返回时回调
     *
     * @param result 函数返回值
     */
    void onMethodResult(String result);
}

客户端,设置发送文字调用Js方法,设置输入内容

//调用无返回值的Js方法
mSend.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        String str = mInput.getText().toString();
        mWebView.loadUrl("javascript:if(window.remote){window.remote('" + str + "')}");
    }
});

Web端提供的Js方法,设置输入内容

//提供给客户端调用,设置输入文字
var remote = function (str) {
    inputEle.value = str;
}
//调动有返回值的Js方法
mCallJsParamsMethod.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        final OnMethodResultCallback onCall = new OnMethodResultCallback() {
            @Override
            public void onMethodResult(String result) {
                toast("收到调用的Js方法返回值: " + result);
            }
        };
        if (isOvertopKitkat()) {
            //4.4以上可以直接用
            mWebView.evaluateJavascript("javascript:remoteWithValue();", new ValueCallback<String>() {
                @Override
                public void onReceiveValue(String value) {
                    onCall.onMethodResult(value);
                }
            });
        } else {
            mCompatKitkatJsMethodCallbackMap.put(SETUP_HTML, onCall);
            //4.4以下,使用prompt将信息带出去,受到信息再拦截掉
            mWebView.loadUrl("javascript:prompt(remoteWithValue());");
        }
    }
});

mWebView.setWebChromeClient(new WebChromeClient() {
    @Override
    public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
        //拦截掉,回调4.4一下的Js返回值处理
        OnMethodResultCallback callback = mCompatKitkatJsMethodCallbackMap.get(url);
        if (callback != null) {
            callback.onMethodResult(message);
            mCompatKitkatJsMethodCallbackMap.remove(url);
            //拦截后必须取消掉,不发送到客户端WebView,不调用也不行,会阻塞等待结果
            result.cancel();
            return true;
        } else {
            return super.onJsPrompt(view, url, message, defaultValue, result);
        }
    }
});

/**
 * 版本号是否大于等于4.4
 */
protected boolean isOvertopKitkat() {
    return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
}
上一篇 下一篇

猜你喜欢

热点阅读