概述
今天来看看WebView和Js的交互功能如何实现
效果图
代码
首先我们需要准备一个html文件,放在assets目录中,方便我们从代码中加载,html的代码如下
<html> <head> <title>WebView和Js交互</title> <script type="text/javascript"> function updateHtml(){ document.getElementById("content").innerHTML = "android调用了js的update方法"; } </script> </head> <body> <a onClick="window.android.showToast()" href="#">调用android方法</a><br/> <span id="content"></span> </body> </html>
updateHtml方法是被Android调用的,用来更新当前页面的内容
a标签里我们指定了一个onClick属性,它的值是window.android.showToast(),其中android是我们定义的一个对象,通过下面的代码指定
webView.addJavascriptInterface(new JavaScriptInterface(),"android");
addJavaScriptInterface这个方法接收两个参数,第一个指定注入到JavaScript中的Java对象,第二个参数是这个参数的名字,通过window.name.methodName,我们就可以在js中调用android的方法了
showToast是我们在Android中定义的一个方法,Js调用
/** * webview和js交互 */ public class WebJsActivity extends AppCompatActivity { private static final String TAG = "WebJsActivity"; private WebView webView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_web_js); webView = (WebView) findViewById(R.id.webview); webView.getSettings().setJavaScriptEnabled(true); webView.loadUrl("file:///android_asset/demo.html"); webView.addJavascriptInterface(new JavaScriptInterface(),"android"); public void updateJs(View view) { // webView.loadUrl("javascript:updateHtml()");//js方法无返回值 //有返回值处理 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { webView.evaluateJavascript("javascript:updateHtml()", new ValueCallback<String>() { @Override public void onReceiveValue(String value) { System.out.println("==========" + value); } }); } else { //19之前java调用js方法,js在调用java方法把返回值传过来 } } public class JavaScriptInterface{ @JavascriptInterface public void showToast(){ Toast.makeText(WebJsActivity.this, "被JS调用了", Toast.LENGTH_SHORT).show(); } } }
这里我们注意到,Android中的方法加入了一个@JavascriptInterface注解,因为js可以通过反射调用我们的android代码,这样是有安全隐患的,谷歌为了安全,从API17开始,只有添加了这个注解的java方法才能够被js调用,那么API17以下我们想要实现交互,又不发生安全漏洞,要怎么做呢?其实js还有另一种方式调用android代码,
我们稍微修改下js的代码
<html> <head> <title>WebView和Js交互</title> <script type="text/javascript"> function updateHtml(){ document.getElementById("content").innerHTML = "android调用了js的update方法"; return "success"; } function callAndroid(){ document.location = "abc://webview?age=1&name=lxn"; } </script> </head> <body> <a onClick="callAndroid()" href="#">调用android方法</a><br/> <span id="content"></span> </body> </html>这里的主要修改是当我们在js中调用android方法是,不是直接去掉用定义好的某个方法,而是指定的一个协议,进行重定向,然后我们就可以在WebViewClient的shouldOverideUrlLoading方法中进行拦截处理的,代码如下
//WebChromeClient主要处理js的对话框 webView.setWebChromeClient(new WebChromeClient(){ @Override public boolean onJsAlert(WebView view, String url, String message, JsResult result) { return super.onJsAlert(view, url, message, result); } @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { return super.onJsPrompt(view, url, message, defaultValue, result); } }); //WebViewClient主要处理各种通知,事件 webView.setWebViewClient(new WebViewClient(){ @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { checkUri(Uri.parse(url)); return true; } }); } public void checkUri(Uri uri){ Log.d(TAG, "shouldOverrideUrlLoading: " + uri.getScheme()); Log.d(TAG, "shouldOverrideUrlLoading: " + uri.getAuthority());//webview String age = uri.getQueryParameter("age");//1 String name = uri.getQueryParameter("name");//lxn Log.d(TAG, "shouldOverrideUrlLoading: " + age +"--"+name); if (uri.getScheme().equals("abc")) { //这里调用android的方法 Toast.makeText(this, "通过url调用", Toast.LENGTH_SHORT).show(); } }