目錄
- 二、具體分析
- 2.1 Android通過WebView調用 JS 代碼
- 方式1:通過WebView的loadUrl()
- 方式2:通過WebView的evaluateJavascript()
- 方法對比
- 使用建議
- 2.2、JS通過WebView調用 Android 代碼
- 2.2.1、方法分析
- 方式1:通過 WebView的addJavascriptInterface()進行對象映射
- 方式2:通過 WebViewClient 的方法shouldOverrideUrlLoading ()回調攔截 url
- 方式3:通過 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回調攔截JS對話框alert()、confirm()、prompt() 消息
- 三種方式的對比 & 使用場景
相關文章閱讀:
- Android WebView使用攻略
Github下載地址:Android WebView與JS互調
# 一、交互方式總結 Android與JS通過WebView互相調用方法,實際上是:
-
Android去調用JS的代碼;
-
JS去調用Android的代碼;
二者溝通的橋梁是WebView
對于Android調用JS代碼的方法有2種:
-
通過WebView的
loadUrl()
; -
通過WebView的
evaluateJavascript()
;
對于JS調用Android代碼的方法有3種:
-
通過
WebView
的addJavascriptInterface()
進行對象映射; -
通過
WebViewClient
的shouldOverrideUrlLoading()
方法回調攔截 url; -
通過
WebChromeClient
的onJsAlert()
、onJsConfirm()
、onJsPrompt()
方法回調攔截JS
對話框alert()
、confirm()
、prompt()
消息;
二、具體分析
2.1 Android通過WebView調用 JS 代碼
對于Android調用JS代碼的方法有2種:
-
通過WebView的
loadUrl()
; -
通過WebView的
evaluateJavascript()
;
方式1:通過WebView的loadUrl()
-
實例介紹:點擊Android按鈕,即調用WebView JS(文本名為javascript)中callJS()
-
具體使用:
步驟1:將需要調用的JS代碼以.html
格式放到src/main/assets文件夾里
1、為了方便展示,本文是采用Andorid調用本地JS代碼說明;
2、實際情況時,Android更多的是調用遠程JS代碼,即將加載的JS代碼路徑改成url即可
需要加載JS代碼:androidcalljs.html
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Java調用JS方法</title><!-- JS代碼 --><script><!-- Android需要調用的方法 -->function callJS(){alert("Android調用了JS的callJS方法");}</script></head>
</html>
步驟2:在Android里通過WebView設置調用JS代碼
Android代碼:AndroidCallJS1.java
package com.davis.webviewjs.demo;import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.Button;public class AndroidCallJS1 extends AppCompatActivity {private Button btn;private WebView webview;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_webview);init();}private void init(){btn = (Button)findViewById(R.id.btn);webview = (WebView)findViewById(R.id.webview);WebSettings webSettings = webview.getSettings();// 設置與Js交互的權限webSettings.setJavaScriptEnabled(true);// 設置允許JS彈窗webSettings.setJavaScriptCanOpenWindowsAutomatically(true);// 先載入JS代碼// 格式規定為:file:///android_asset/文件名.htmlwebview.loadUrl("file:///android_asset/androidcalljs.html");btn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {webview.post(new Runnable() {@Overridepublic void run() {// 注意調用的JS方法名要對應上// 調用javascript的callJS()方法webview.loadUrl("javascript:callJS()");}});}});// 由于設置了彈窗檢驗調用結果,所以需要支持js對話框// webview只是載體,內容的渲染需要使用webviewChromClient類去實現// 通過設置WebChromeClient對象處理JavaScript的對話框//設置響應js 的Alert()函數webview.setWebChromeClient(new WebChromeClient() {@Overridepublic boolean onJsAlert(WebView view, String url, String message, final JsResult result) {AlertDialog.Builder b = new AlertDialog.Builder(AndroidCallJS1.this);b.setTitle("Alert");b.setMessage(message);b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {result.confirm();}});b.setCancelable(false);b.create().show();return true;}});}
}
特別注意:JS代碼調用一定要在 onPageFinished()
回調之后才能調用,否則不會調用。
onPageFinished()
屬于WebViewClient
類的方法,主要在頁面加載結束時調用
方式2:通過WebView的evaluateJavascript()
- 優點:
該方法比第一種方法效率更高、使用更簡潔
。
1、因為該方法的執行不會使頁面刷新,而第一種方法(loadUrl )的執行則會。
2、Android 4.4 后才可使用
- 具體使用
// 只需要將第一種方法的loadUrl()換成下面該方法即可
webview.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {@Overridepublic void onReceiveValue(String value) {//此處為 js 返回的結果Log.i("WEBVIEW_JS", "value : " + value);}
});
方法對比
調用方式 | 優點 | 缺點 | 使用場景 |
---|---|---|---|
使用loadUrl() | 方便簡潔 | 效率低; 獲取返回值麻煩; | 不需要獲取返回值,對性能要求較低時 |
使用evaluateJavascript() | 效率高 | 向下兼容性差(僅Android 4.4以上可用) | Android 4.4以上 |
使用建議
兩種方法混合使用,即Android 4.4以下使用方法1,Android 4.4以上方法2
// Android版本變量
final int version = Build.VERSION.SDK_INT;
// 因為該方法在 Android 4.4 版本才可使用,所以使用時需進行版本判斷
if (version < 18) {webview.loadUrl("javascript:callJS()");
} else {webview.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {@Overridepublic void onReceiveValue(String value) {//此處為 js 返回的結果}});
}
2.2、JS通過WebView調用 Android 代碼
對于JS調用Android代碼的方法有3種:
-
通過
WebView
的addJavascriptInterface()
進行對象映射 -
通過
WebViewClient
的shouldOverrideUrlLoading()
方法回調攔截 url -
通過
WebChromeClient
的onJsAlert()
、onJsConfirm()
、onJsPrompt()
方法回調攔截JS對話框alert()
、confirm()
、prompt()
消息
2.2.1、方法分析
方式1:通過 WebView的addJavascriptInterface()進行對象映射
步驟1:定義一個與JS對象映射關系的Android類:AndroidtoJs
AndroidtoJs.java(注釋已經非常清楚)
// 繼承自Object類
public class AndroidtoJs extends Object {// 定義JS需要調用的方法// 被JS調用的方法必須加入@JavascriptInterface注解@JavascriptInterfacepublic void hello(String msg) {System.out.println("JS調用了Android的hello方法");}
}
步驟2:將需要調用的JS代碼以.html格式放到src/main/assets文件夾里
需要加載JS代碼:jscallandroid.html
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>JS調用Java方法</title><script>function callAndroid(){<!-- 由于對象映射,所以調用test對象等于調用Android映射的對象 -->test.hello("js調用了android中的hello方法");}</script></head><body><!-- 點擊按鈕則調用callAndroid函數 --><button type="button" id="button1" onclick="callAndroid()"></button></body>
</html>
步驟3:在Android里通過WebView設置Android類與JS代碼的映射
package com.davis.webviewjs.demo;import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.Button;public class JSCallAndroid1 extends AppCompatActivity {private Button btn;private WebView webview;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_webview);init();}private void init() {btn = (Button)findViewById(R.id.btn);btn.setVisibility(View.GONE);webview = (WebView)findViewById(R.id.webview);WebSettings webSettings = webview.getSettings();// 設置與Js交互的權限webSettings.setJavaScriptEnabled(true);// 設置允許JS彈窗webSettings.setJavaScriptCanOpenWindowsAutomatically(true);// 通過addJavascriptInterface()將Java對象映射到JS對象//參數1:Javascript對象名//參數2:Java對象名webview.addJavascriptInterface(new AndroidtoJs(), "test");//AndroidtoJS類對象映射到js的test對象// 先載入JS代碼// 格式規定為:file:///android_asset/文件名.htmlwebview.loadUrl("file:///android_asset/jscallandroid.html");}
}
特點
- 優點:使用簡單
僅將Android對象和JS對象映射即可
- 缺點:存在嚴重的漏洞問題
方式2:通過 WebViewClient 的方法shouldOverrideUrlLoading ()回調攔截 url
具體原理:
-
Android通過 WebViewClient 的回調方法shouldOverrideUrlLoading ()攔截 url
-
解析該 url 的協議
-
如果檢測到是預先約定好的協議,就調用相應方法
即JS需要調用Android的方法
具體使用:
步驟1:在JS約定所需要的Url協議
JS代碼:jscallandroid2.html
以.html格式放到src/main/assets文件夾里
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Java調用JS方法</title><!-- JS代碼 --><script>function callAndroid(){<!-- 約定的url協議為:js://webview?arg1=111&arg2=222 -->document.location = "js://webview?arg1=111&arg2=222";}</script></head><body><!-- 點擊按鈕則調用callAndroid函數 --><button type="button" id="button1" onclick="callAndroid()">JS調用Android方法</button></body></html>
當該JS通過Android的webview.loadUrl("file:///android_asset/jscallandroid2.html")
加載后,就會回調shouldOverrideUrlLoading()
,接下來繼續看步驟2:
步驟2:在Android通過WebViewClient復寫shouldOverrideUrlLoading()
JSCallAndroid2.java
package com.davis.webviewjs.demo;import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.webkit.WebResourceRequest;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;import java.util.HashMap;
import java.util.Set;public class JSCallAndroid2 extends AppCompatActivity {private Button btn;private WebView webview;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_webview);init();}private void init() {btn = (Button)findViewById(R.id.btn);btn.setVisibility(View.GONE);webview = (WebView)findViewById(R.id.webview);WebSettings webSettings = webview.getSettings();// 設置與Js交互的權限webSettings.setJavaScriptEnabled(true);// 設置允許JS彈窗webSettings.setJavaScriptCanOpenWindowsAutomatically(true);// 先載入JS代碼// 格式規定為:file:///android_asset/文件名.htmlwebview.loadUrl("file:///android_asset/jscallandroid2.html");webview.setWebViewClient(new WebViewClient(){@Overridepublic 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所需要調用的邏輯Log.i("WEBVIEW_JS","js調用了Android的方法");// 可以在協議上帶有參數并傳遞到Android上HashMap<String, String> params = new HashMap<>();Set<String> collection = uri.getQueryParameterNames();Log.i("WEBVIEW_JS", "URI : " + uri);}return true;}return super.shouldOverrideUrlLoading(view, url);}});}
}
特點
-
優點:不存在方式1的漏洞;
-
缺點:JS獲取Android方法的返回值復雜。
如果JS想要得到Android方法的返回值,只能通過 WebView 的 loadUrl ()去執行 JS 方法把返回值傳遞回去,相關的代碼如下:
// Android:MainActivity.java
webview.loadUrl("javascript:returnResult(" + result + ")");// JS:javascript.html
function returnResult(result){alert("result is" + result);
}
方式3:通過 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回調攔截JS對話框alert()、confirm()、prompt() 消息
在JS中,有三個常用的對話框方法:
方法 | 作用 | 返回值 | 備注 |
---|---|---|---|
alert() | 彈出警告框 | 沒有 | 在文本加入\n可換行 |
confirm() | 彈出確認框 | 兩個返回值 | - 返回布爾值; - 通過該值可判斷點擊時確認還是取消:true表示確認,false表示取消。 |
prompt() | 彈出輸入框 | 任意設置返回值 | - 點擊【確認】,返回輸入框的的值; - 點擊【取消】,返回null。 |
方式3的原理:Android通過 WebChromeClient
的onJsAlert()
、onJsConfirm()
、onJsPrompt()
方法回調分別攔截JS對話框
(即上述三個方法),得到他們的消息內容,然后解析即可。
下面的例子將用 攔截JS的輸入框(即prompt()方法) 說明 :
1、常用的攔截是:攔截 JS的輸入框(即prompt()方法)
2、因為只有prompt()可以返回任意類型的值,操作最全面方便、更加靈活;而alert()對話框沒有返回值;confirm()對話框只能返回兩種狀態(確定 / 取消)兩個值;
步驟1:加載JS代碼,如下:
jscallandroid3.html
以.html格式放到src/main/assets文件夾里
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Java調用JS方法</title><!-- JS代碼 --><script><!-- Android需要調用的方法 -->function callJS(){var result = prompt("js://webview?arg1=111&arg2=222");alert("result : " + result);}</script></head><body><!-- 點擊按鈕則調用callAndroid函數 --><button type="button" id="button1" onclick="callJS()">JS調用Android方法</button></body></html>
當使用webview.loadUrl("file:///android_asset/jscallandroid3.html")
加載了上述JS代碼后,就會觸發回調onJsPrompt()
,具體如下:
1、如果是攔截警告框(即alert()),則觸發回調onJsAlert();
2、如果是攔截確認框(即confirm()),則觸發回調onJsConfirm();
步驟2:在Android通過WebChromeClient
復寫onJsPrompt()
package com.davis.webviewjs.demo;import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.LinearLayout;public class JSCallAndroid3 extends AppCompatActivity {private Button btn;private WebView webview;private LinearLayout layout;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_webview);init();}private void init() {btn = (Button)findViewById(R.id.btn);btn.setVisibility(View.GONE);webview = (WebView)findViewById(R.id.webview);WebSettings webSettings = webview.getSettings();// 設置與Js交互的權限webSettings.setJavaScriptEnabled(true);// 設置允許JS彈窗webSettings.setJavaScriptCanOpenWindowsAutomatically(true);// 先載入JS代碼// 格式規定為:file:///android_asset/文件名.htmlwebview.loadUrl("file:///android_asset/jscallandroid3.html");webview.setWebChromeClient(new WebChromeClient(){@Overridepublic boolean onJsAlert(WebView view, String url, String message, JsResult result) {// 根據協議的參數,判斷是否是所需要的url(原理同方式2)// 一般根據scheme(協議格式) & authority(協議名)判斷(前兩個參數)//假定傳入進來的 url = "js://webview?arg1=111&arg2=222"(同時也是約定好的需要攔截的)Log.i("WEBVIEW_JS", "message : " + message);Uri uri = Uri.parse(message);// 如果url的協議 = 預先約定的 js 協議// 就解析往下解析參數if ( uri.getScheme().equals("js")) {// 如果 authority = 預先約定協議里的 webview,即代表都符合約定的協議// 所以攔截url,下面JS開始調用Android需要的方法if (uri.getAuthority().equals("webview")) {//Log.i("WEBVIEW_JS", "js調用了Android的方法");// 攔截alert()之后必須調用result.confirm()方法,否則會出現白屏result.confirm();return true;}}return super.onJsAlert(view, url, message, result);}@Overridepublic boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {// 根據協議的參數,判斷是否是所需要的url(原理同方式2)// 一般根據scheme(協議格式) & authority(協議名)判斷(前兩個參數)//假定傳入進來的 url = "js://webview?arg1=111&arg2=222"(同時也是約定好的需要攔截的)Log.i("WEBVIEW_JS", "message : " + message);Uri uri = Uri.parse(message);// 如果url的協議 = 預先約定的 js 協議// 就解析往下解析參數if ( uri.getScheme().equals("js")) {// 如果 authority = 預先約定協議里的 webview,即代表都符合約定的協議// 所以攔截url,下面JS開始調用Android需要的方法if (uri.getAuthority().equals("webview")) {//Log.i("WEBVIEW_JS", "js調用了Android的方法");// 攔截alert()之后必須調用result.confirm()方法,否則會出現白屏result.confirm("js調用了Android的方法");return true;}}return super.onJsPrompt(view, url, message, defaultValue, result);}@Overridepublic boolean onJsConfirm(WebView view, String url, String message, JsResult result) {return super.onJsConfirm(view, url, message, result);}});}@Overrideprotected void onDestroy() {if (webview != null) {webview.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);webview.clearHistory();((ViewGroup) webview.getParent()).removeView(webview);webview.destroy();webview = null;}super.onDestroy();}
}
三種方式的對比 & 使用場景
調用方式 | 優點 | 缺點 | 使用場景 |
---|---|---|---|
通過addJavascriptInterface()進行添加對象映射 | 方便簡潔 | Android 4.2以下存在漏洞問題 | Android 4.2以上相對簡單的互調場景 |
通過WebVeiw的方法shouldOverrideUrlLoading()回調攔截url | 不存在漏洞問題 | - 使用復雜:需要進行協議的約束; - 從Native層往Web層傳遞值比較繁瑣 | 不需要返回值情況下的互調場景 |
通過WebChromeClient的onJsAlert()、onJsConfirm()、onJsPrompt()方法回調攔截JS對話框消息 | 不存在漏洞問題 | 使用復雜:需要進行協議的約束; | 能滿足大多數情況下的互調場景 |