上一篇给大家说了下WebView调用js这个应该来说比较简单,在说JS调用WebView之前如果有没看过上篇的这里有传送门,好了接下来还是直接上代码吧!
对于JS调用Android代码的方法大体来说有三种:
1,通过WebView的addJavaScriptInterface()就是对象映射的方式(虽然有瑕疵,但是这是本人非常常用的方法)
2,通过WebViewClient的shouldOverrideUrlLoading()方法回调拦截url
3,通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、
confirm()、prompt() 消息
先看第一种通过WebView的addJavaScriptInterface()
/步骤1:定义一个与JS对象映射关系的Android类:AndroidtoJs public class AndroidtoJs extends Object{ //定义js需要调用的方法(必须加入@JavascriptInterface注解) @JavascriptInterface public void hello(String str){ System.out.println(str); } }
再看看html写法
<html> <head> <meta charset="utf-8"> <title>YANXUN</title> <script> function callAndroid(){ //由于对象映射,所以调用了test对象等于调用了Android映射的对象 test.hello("js调用了Android当中的你好方法"); } </script> </head> <body> <button type="button" id="b1" onclick="callAndroid()" >调用callAndroid()方法</button> <style> button{ width:300px; height:45px; } </style> </body> </html>
看看activity中的写法
public class MainActivity extends AppCompatActivity { private WebView mWebView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mWebView = ((WebView) findViewById(R.id.wv)); WebSettings mWebViewSettings = mWebView.getSettings(); //与js交互的权限 mWebViewSettings.setJavaScriptEnabled(true); //通过WebView的addJavascriptInterface()进行对象映射 方式使用简单. //但是该方法会存在严重漏洞:当JS拿到Android这个对象后,就可以调用这个Android对象中所有的方法(java反射机制),包括系统类(java.lang.Runtime 类),从而进行任意代码执行。 /**例如:可以执行命令获取本地设备的SD卡中的文件等信息从而造成信息泄露 *当一些APP通过扫描二维码打开一个外部网页时,攻击者就可以执行这段 js 代码进行漏洞攻击 * 在微信盛行、扫一扫行为普及的情况下,该漏洞的危险性非常大 */ /*参数一:一个与JS对象映射关系的Android类 *参数二:JS中进行映射的对象 */ mWebView.addJavascriptInterface(new AndroidtoJs(),"test");//AndroidtoJS类对象映射到js的test对象 //加载h5,注意格式为:file:///android_asset/文件名 mWebView.loadUrl("file:///android_asset/MyJs.html"); } }
后面的布局都是这个
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="yanxun.com.jstowebview.MainActivity"> <WebView android:layout_width="match_parent" android:layout_height="250dp" android:background="#ffffff" android:layout_margin="10dp" android:id="@+id/wv" /> </LinearLayout>
---------------------------------------------------------------------------------------------------------------------------------
第二种就复杂一点了!通过WebViewClient的shouldOverrideUrlLoading()方法回调拦截url
先看看html代码
<html> <head> <meta charset="utf-8"> <title>Lebron James</title> <script> function callAndroid(){ /*约定的url协议为:js://webview?arg1=111&arg2=222*/ document.location = "js://webview?arg1=111&arg2=222"; } </script> </head> <body> <button type="button" id="button1" onclick="callAndroid()">点击调用Android代码</button> <style> button{ width:300px; height:45px; } </style> </body> </html>
再来看看activity写法
public class Main2Activity extends AppCompatActivity { private WebView mWv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2); mWv = ((WebView) findViewById(R.id.wv)); WebSettings webSettings = mWv.getSettings(); webSettings.setJavaScriptEnabled(true); webSettings.setJavaScriptCanOpenWindowsAutomatically(true); mWv.loadUrl("file:///android_asset/MyJsTwo.html"); //1.重写shouldOverrideUrlLoading方法 优点是没有方法一的漏洞但获取Android方法的返回值复杂。 mWv.setWebViewClient(new WebViewClient(){ @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { // 步骤2:根据协议的参数,判断是否是所需要的url // 一般根据scheme(协议格式) & authority(协议名)判断(前两个参数) //假定传入进来的 url = "js://webview?arg1=111&arg2=222"(同时也是约定好的需要拦截的) Uri uri = Uri.parse(url); // 如果url的协议 = 预先约定的 js 协议 // 就解析往下解析参数 if(uri.getScheme().equals("js")){ // 如果 authority = 预先约定协议里的 webview,即代表都符合约定的协议 // 所以拦截url,下面JS开始调用Android需要的方法 if(uri.getAuthority().equals("webview")){ // 步骤3: // 执行JS所需要调用的逻辑 System.out.println("js调用了Android的方法"); // 可以在协议上带有参数并传递到Android上 /* HashMap<String, String> params = new HashMap<>(); Set<String> collection = uri.getQueryParameterNames();*/ } return true; } return super.shouldOverrideUrlLoading(view, url); } }); } }
---------------------------------------------------------------------------------------------------------------------------------
第三种则是通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、
confirm()、prompt() 消息
先看看html的写法
<html> <head> <meta charset="UTF-8"> <title>Kobe bryant</title> <script> function clickprompt(){ //调用prompt() var result = prompt("js://demo?arg1=111&arg2=222"); alert("demo" + result); } </script> </head> <body> <button type="button" id="button1" onclick="clickprompt()">点击调用Android代码</button> </body> <style> button{ width:300px; height:45px; } </style> </html>
再看看activity写法
public class Main3Activity extends AppCompatActivity { private WebView mV; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main3); mV = ((WebView) findViewById(R.id.wv)); WebSettings mVSettings = mV.getSettings(); mVSettings.setJavaScriptEnabled(true); mVSettings.setJavaScriptCanOpenWindowsAutomatically(true); mV.loadUrl("file:///android_asset/MyJsThree.html"); mV.setWebChromeClient(new WebChromeClient(){ /* // 拦截输入框(原理同方式2) // 参数message:代表promt()的内容(不是url) // 参数result:代表输入框的返回值 */ @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { Uri uri = Uri.parse(url); if(uri.getScheme().equals("js")){ if(uri.getAuthority().equals("demo")){ // // 执行JS所需要调用的逻辑 System.out.println("js调用了Android的方法"); //参数result:代表消息框的返回值(输入值) result.confirm("js调用了Android的方法成功啦"); } return true; } return super.onJsPrompt(view, url, message, defaultValue, result); } }); } }好了以上就是三种js调用webview方法详细,注释也算很齐全,相信大家能理解,最后我来总结一下:
方式 | 优点 | 缺点 | 使用地方 |
通过WebView的addJavaScriptInterface() | 使用简单 | 4.2以下有漏洞问题 | 4.2以上比较简单需求 |
通过WebViewClient的shouldOverrideUrlLoading()方法回调拦截url | 没有漏洞 | 使用麻烦,需要双方约定协议,从本地往web传值麻烦 | 不需要返回值的时候 |
通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、 confirm()、prompt() 方法 |
没有漏洞 | 需要双方约定协议 | 能满足大多数 |