我们先看下JS代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Js与Android交互</title>
</head>
<body>
<H2>点击获取按钮,弹出输入框内容</H2>
<div><span>输入数据:</span><input type="text" id="input"/></div>
<div id="btn"><span class="btn">点我获取数据</span></div>
<script type="text/javascript">
var inputData = document.getElementById("input")
var btn = document.getElementById("btn")
//Js调用Android方法
btn.addEventListener("click", function () {
var getInputData = inputData.value;
//androidJs这个方法必须和Android里面的方法保持一致
if (window.androidJs) {
//getJsData这个方法必须和Android里面的方法保持一致
androidJs.getJsData(getInputData)
} else {
alert("与android互调方法不存在!")
}
});
//Android调用Js的方法,writeInputText此方法需要跟android里面的方法保持一致
var writeInputText = function (str) {
inputData.value = str;
//带返回值的JS方法
return '互调成功!'
}
</script>
</body>
</html>
再来看下android代码:
package kotlin.yhsh.cn.androidandjs;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
/**
* @author DELL
* 2019年7月23日19:34:25
*/
public class MainActivity extends Activity {
private TextView tvShowData;
private WebView wvLoadHtml;
private EditText etInputToJs;
private Button btClickInputToJs;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvShowData = findViewById(R.id.tv_show_data);
wvLoadHtml = findViewById(R.id.wv_load_html);
etInputToJs = findViewById(R.id.et_input_to_js);
btClickInputToJs = findViewById(R.id.bt_click_input_to_js);
btClickInputToJs.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String inputToJs = etInputToJs.getText().toString().trim();
//if(window.writeInputText)判断Js里面的writeInputText是否存在,存在就传递inputToJs这个值到Js里面
//wvLoadHtml.loadUrl("javascript:if(window.writeInputText){window.writeInputText('" + inputToJs + "')}");
//下面方法是Android4.4以后才可以用
wvLoadHtml.evaluateJavascript("javascript:if(window.writeInputText){window.writeInputText('" + inputToJs + "')}", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
Log.e("打印互调", value + "=");
}
});
}
});
WebSettings settings = wvLoadHtml.getSettings();
settings.setJavaScriptEnabled(true);
//方法一:androidJs这个方法必须和Js里面的方法保持一致
wvLoadHtml.addJavascriptInterface(new OpenWebView(), "androidJs");
//方法二:
// wvLoadHtml.addJavascriptInterface(new GetWebViewData() {
// @Override
// @JavascriptInterface
// public void getJsData(String data) {
// getFinallyData(data);
// }
// }, "androidJs");
//加载HTML
wvLoadHtml.loadUrl("file:///android_asset/loadAndroid.html");
}
/**
* Js的回调结果方法
*/
class OpenWebView {
/**
* getJsData这个方法必须和Js里面的方法保持一致
*
* @param data Js返回的数据
*/
@JavascriptInterface
public void getJsData(final String data) {
getFinallyData(data);
}
}
/**
* getJsData这个方法必须和Js里面的方法保持一致
*/
interface GetWebViewData {
/**
* Js返回的数据
*
* @param data 数据
*/
void getJsData(String data);
}
private void getFinallyData(final String data) {
Log.e("打印拿到的数据", data);
//当前是子线程
runOnUiThread(new Runnable() {
@Override
public void run() {
tvShowData.setText(data);
}
});
}
}
再来看下xml布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<WebView
android:id="@+id/wv_load_html"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="0dp"/>
<TextView
android:text="Js调用Android后显示的数据"
android:gravity="center"
android:id="@+id/tv_show_data"
android:layout_width="match_parent"
android:layout_height="100dp"/>
<View
android:background="@color/colorPrimary"
android:layout_width="match_parent"
android:layout_height="1dp"/>
<EditText
android:layout_marginTop="10dp"
android:id="@+id/et_input_to_js"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:text="android调用Js代码"
android:id="@+id/bt_click_input_to_js"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
来看下效果图:
JS调用android:
在HTML页面输入数据,然后在最下面的textView控件显示输入内容
再看下android调用JS:
看不懂的可自行下载源码:源码下载
另外附加GitHub下载地址:Android与JS互调源码下载
总结如下:
Android与 js 是如何交互的
在 Android 中,Android 与js 的交互分为两个方面:Android 调用 js 里的方法、js 调用 Android 中的方法;
Android调js。 Android 调 js 有两种方法:
-
WebView.loadUrl("javascript:js代码")。 这种方法的优点是很简洁,缺点是没有返回值,如果需要拿到js方法的返回值则需要js调用Android中的方法来拿到这个返回值;
-
WebView.evaluateJavaScript("javascript:js中的方法名",ValueCallback)。 这种方法比 loadUrl 好的是可以通过 ValueCallback 这个回调拿到 js方法的返回值。缺点是这个方法 Android4.4 才有,兼容性较差。不过放在 2018 年来说,市面上绝大多数 App 都要求最低版本是 4.4 了,所以我认为这个兼容性问题不大;
js 调 Android。 js 调 Android有三种方法:
-
WebView.addJavascriptInterface()。 这是官方解决 js 调用 Android 方法的方案,需要注意的是要在供 js 调用的 Android 方法上加上 @JavascriptInterface 注解,以避免安全漏洞。这种方案的缺点是 Android4.2 以前会有安全漏洞,不过在 4.2 以后已经修复了。同样,在 2018 年来说,兼容性问题不大;
-
重写 WebViewClient的shouldOverrideUrlLoading()方法来拦截url, 拿到 url 后进行解析,如果符合双方的规定,即可调用 Android 方法。优点是避免了 Android4.2 以前的安全漏洞,缺点也很明显,无法直接拿到调用 Android 方法的返回值,只能通过 Android 调用 js 方法来获取返回值;
-
重写 WebChromClient 的 onJsPrompt() 方法,同前一个方式一样,拿到 url 之后先进行解析,如果符合双方规定,即可调用Android方法。最后如果需要返回值,通过 result.confirm("Android方法返回值") 即可将 Android 的返回值返回给 js。方法的优点是没有漏洞,也没有兼容性限制,同时还可以方便的获取 Android 方法的返回值。其实这里需要注意的是在 WebChromeClient 中除 了 onJsPrompt 之外还有 onJsAlert 和 onJsConfirm 方法。那么为什么不选择另两个方法呢?原因在于 onJsAlert 是没有返回值的,而 onJsConfirm 只有 true 和 false 两个返回值,同时在前端开发中 prompt 方法基本不会被调用,所以才会采用onJsPrompt;