現(xiàn)在越來越多的App是混合開發(fā)糟描,很需要原生與網頁交互渗磅,下面介紹如何交互以及有可能出現(xiàn)的坑。
Java調用JS方法
對于Android調用JS方法的方式有2種:
- 通過
WebView
的loadUrl()
- 通過
WebView
的evaluateJavascript()
方式一:loadUrl()
- 第一步:設置與Js交互的權限
WebSettings webSettings = mWebView.getSettings();
// 設置與Js交互的權限
webSettings.setJavaScriptEnabled(true);
- 第二步:設置加載網頁
mWebview.loadUrl("加載的網頁");
- 第三步:調用JS方法
通過使用mWebview.loadUrl 調用“javascript:" + 方法
如果JS這樣定義的
<script>
function callJS(){
alert("Android調用了JS的無參callJS方法");
}
function callJSParameter(message){
alert("Android調用JS的有參callJS方法的圆,參數(shù)為"+message);
}
</script>
在Java代碼中
//調用無參
mWebView.loadUrl("javascript:callJS()");
//調用有參
mWebView.loadUrl("javascript:callJSParameter('測試')");
方式一注意事項(也就是坑)
- 調用JS方法時一定要在
onPageFinished()
回調之后才能調用力九,否則不會調用。
onPageFinished()屬于WebViewClient類的方法塞赂,主要在頁面加載結束時調用
- 調用JS方法時一定要在主線程泪勒,否則會崩潰異常。
java.lang.RuntimeException: java.lang.Throwable: A WebView method was called on thread 'Thread-18022'. All WebView methods must be called on the same thread.
如果非要在子線程調用减途,也要將其轉換到主線程中去:
mWebView.post(new Runnable() {
@Override
public void run() {
mWebView.loadUrl("javascript:callJS()");
}
});
- 調用的帶參數(shù)的JS方法時酣藻,參數(shù)類型如果為String ,切記使用單引號( ') 包裹鳍置;如果為數(shù)組類型則不用辽剧,如:javascript:callJs([01, 02, 03]);如果為其他復雜類型則可以轉換為 Json 字符串的形式傳遞税产。
方式二:evaluateJavascript()
*執(zhí)行該方法不會使頁面刷新怕轿,而第一種方法(loadUrl )的執(zhí)行則會,但是Android 4.4 后才可使用辟拷,兼容性要求偏高撞羽。
JS代碼
function callJSReturn(message){
return message;
}
Java代碼
mWebView.evaluateJavascript("javascript:callJSReturn('方式二調用JS')", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//此處為 js 返回的結果
Toast.makeText(MainActivity.this, value, Toast.LENGTH_SHORT).show();
}
});
兩種方式對比
調用方式 | 優(yōu)點 | 缺點 | 使用場景 |
---|---|---|---|
loadUrl() |
兼容性好 | 獲取返回值麻煩 | 不需要獲取返回值 |
evaluateJavascript() |
只能從4.4(18)以上使用 | 獲取返回值簡單 | 4.4(18)以上 |
JS調用Java
JS調用Java的方式有三種
- 通過
WebView
的addJavascriptInterface()
- 通過
WebViewClient
的shouldOverrideUrlLoading()
方法回調攔截url
- 通過
WebChromeClient
的onJsAlert()
、onJsConfirm()
衫冻、onJsPrompt()
方法回調攔截JS對話框alert()
诀紊、confirm()
、prompt()
消息
方式一:addJavascriptInterface()
JS代碼
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>LYJ</title>
<script>
function testCallAndroid(){
test.callAndroid("JS調用了Android方法");
}
</script>
</head>
<body>
<button type="button" id="button1" onclick="testCallAndroid()">點擊按鈕通過方式一調用Android方法</button>
</body>
</html>
- 第一步:定義一個JS調用的方法
@JavascriptInterface
public void callAndroid(String message){
Toast.makeText(CallAndroidActivity.this,message,Toast.LENGTH_SHORT).show();
}
1.必須要有@JavascriptInterface隅俘,在 Android4.2(17) 及更高版本的系統(tǒng)中邻奠,任何暴露給 Js 訪問的 Java 接口都需要添加這個注解,否則會報異常:Uncaught TypeError: Object [object Object] has no method ‘XXX’为居。系統(tǒng)在之前的版本中有漏洞碌宴,Js 可以通過反射的方式訪問注入 WebView 中的 Java 對象的 public 類型 field 和 method,從而隨意修改宿主程序蒙畴,所以為了安全增加了這個注解贰镣。想更深入的了解參考這篇文章你不知道的 Android WebView 使用漏洞
2.方法名切記要和JS調用的方法名一樣
- 第二步將Java對象映射到JS對象
mWebView.addJavascriptInterface(this, "test");
第一個參數(shù)是寫有JS調用方法的Android對象,第二個參數(shù)是JS調用的對象名膳凝,切記跟網頁上的一致碑隆。
方式一注意事項
- Js 調用 Java 方法時,不是在主線程 (Thread Name:main) 中運行的蹬音,而是在一個名為 JavaBridge 的線程中執(zhí)行的上煤,如果需要 Java 繼續(xù)回調 Js,千萬別在 JavascriptInterface 方法體中直接執(zhí)行 loadUrl() 方法祟绊,而是像前面一樣進行線程切換操作楼入。
方式二:WebViewClient
的shouldOverrideUrlLoading()
方法
JS代碼
<script>
function testTwoCallAndroid(){
document.location = "js://webview?key1=value1&key2=value2";
}
</script>
<body>
<button type="button" id="button2" onclick="testTwoCallAndroid()">點擊按鈕通過方式二調用Android方法</button>
</body>
Java代碼
// 復寫WebViewClient類的shouldOverrideUrlLoading方法
mWebView.setWebViewClient(
new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// 第一步:根據(jù)協(xié)議的參數(shù),判斷是否是所需要的url
// 一般根據(jù)scheme(協(xié)議格式) & authority(協(xié)議名)判斷
//假定傳入進來的 url = "js://webview?key1=value1&key2=value2"(同時也是約定好的需要攔截的)
Uri uri = Uri.parse(url);
// 第二步:如果url的協(xié)議 = 預先約定的 js 協(xié)議牧抽,就解析往下解析參數(shù)
if (uri.getScheme().equals("js")) {
//第三步:再判斷嘉熊,如果 authority = 預先約定協(xié)議里的 webview,即代表都符合約定的協(xié)議
// 所以攔截url,下面JS開始調用Android需要的方法
if (uri.getAuthority().equals("webview")) {
// 第四步:
// 執(zhí)行JS所需要調用的邏輯
// 獲取協(xié)議執(zhí)行的參數(shù)
HashMap<String, String> params = new HashMap<>();
Set<String> collection = uri.getQueryParameterNames();
for (String key : collection){
String value = uri.getQueryParameter(key);
Log.d("==","key = " + key + ",value = " + value);
params.put(key,value);
}
}
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
}
);
}
注釋比較清楚扬舒,就按注釋的步驟走阐肤。
shouldOverrideUrlLoading(WebView view, String url)在Android 7.0(24)過時,官方改為shouldOverrideUrlLoading (WebView view,
WebResourceRequest request)讲坎。然后獲取Uri改為Uri uri = request.getUrl();
孕惜。
方式三:通過WebChromeClient
的onJsAlert()
、onJsConfirm()
晨炕、onJsPrompt()
方法回調攔截JS對話框alert()
衫画、confirm()
、prompt()
消息
方法 | 作用 | 返回值 | 備注 |
---|---|---|---|
alert() | 彈出警告框 | 沒有 | 在文本加入/n可換行 |
confirm() | 彈出確認框 | 兩個 | 返回boolean值,true代表確認瓮栗,false代表取消 |
prompt() | 彈出輸入框 | 任意 | 點擊確認返回輸入框的值削罩,點擊取消返回null |
下面的例子將用攔截 JS的輸入框(即prompt()
方法)說明,其他兩種與其類似 :
JS代碼
<script>
function testPromptCallAndroid(){
var result=prompt("js://prompt?key1=value1&key2=value2");
alert("方式三 " + result);
}
</script>
<body>
<button type="button" id="button3" onclick="testPromptCallAndroid()">點擊按鈕通過方式三調用Android方法</button>
</body>
Java代碼
mWebView.setWebChromeClient(
new WebChromeClient() {
// 攔截輸入框(原理同方式2)
// 參數(shù)message:代表promt()的內容(不是url)
// 參數(shù)result:代表輸入框的返回值
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
// 根據(jù)協(xié)議的參數(shù)费奸,判斷是否是所需要的url(原理同方式2)
// 一般根據(jù)scheme(協(xié)議格式) & authority(協(xié)議名)判斷(前兩個參數(shù))
//假定傳入進來的 url = "js://webview?arg1=111&arg2=222"(同時也是約定好的需要攔截的)
Uri uri = Uri.parse(message);
// 如果url的協(xié)議 = 預先約定的 js 協(xié)議
// 就解析往下解析參數(shù)
if (uri.getScheme().equals("js")) {
// 如果 authority = 預先約定協(xié)議里的 webview弥激,即代表都符合約定的協(xié)議
// 所以攔截url,下面JS開始調用Android需要的方法
if (uri.getAuthority().equals("prompt")) {
// 執(zhí)行JS所需要調用的邏輯
// 可以在協(xié)議上帶有參數(shù)并傳遞到Android上
HashMap<String, String> params = new HashMap<>();
Set<String> collection = uri.getQueryParameterNames();
for (String key : collection) {
String value = uri.getQueryParameter(key);
Log.d("==", "key = " + key + ",value = " + value);
params.put(key, value);
}
//參數(shù)result:代表消息框的返回值(輸入值)
result.confirm("js通過方式三調用了Android的方法成功");
}
return true;
}
return super.onJsPrompt(view, url, message, defaultValue, result);
}
}
);
整體上跟上一個方式相差不多。
其他
- 如果需要回調這種需求愿阐,你只要android的異步回調中微服,使用loadUrl調用js的相關方法。
- 如果需要給JS返回值缨历,除了方式三以外以蕴,還可以這么做
JS代碼
function returnResult(result){
alert("result is" + result);
}
Java代碼
mWebView.loadUrl("javascript:returnResult(" + result + ")");
參考文章
Android:你要的WebView與 JS 交互方式 都在這里了
Android WebView —— Java 與 JavaScript 交互總結
Android_其他語言交互篇——Js、C#戈二、C舒裤、C++