『壹』 h5頁面和app頁面怎麼通訊
通訊方法如下:
URL Scheme 是最常見的方法了,它的核心概念是攔截URL。
APP實現了一個webview,H5在其內打開。
它可以攔截到H5發生的跳轉信息,如URL。如果以URL作為通信依據,就可以隨意約定個URL,如:建立通信:https://__bridge_loaded__ 獲取token:https://nativaAPI_getToken
H5就可以通過跳轉到該地址被APP攔截,APP識別到了約定的URL觸發對應方法。
2. 攔截列印日誌
APP的webview有對應的監聽,可以攔截到js的 alert 等。就可通過輸出 alert("標識", "方法名", "參數") 等方法進行通信。IOS 由於安全限制,UIWebView 性能原因已棄用不考慮,WKWebView 對 alert 等方法做了攔截,需要做代理處理一下即可。Android 通過 WebView.addjavascriptInterface 方法實現。但因 Android 4.2 以前的系統中沒有正確限制使用WebView.addJavascriptInterface,導致攻擊者可以偽造JS橋接調用原生方法,存在安全漏洞,因此較少見。Google在Android 4.2 版本中修復了他,通過在Java方法內的最上面聲明一句@JavascriptInterface,從而避免漏洞攻擊。若想 4.2 版本前使用就要另尋出路,window.prompt 一問一答的機制恰好可以滿足,Android onJsPrompt方法中去解析傳遞過來的文本,將處理結果返回給JS。
3. window注入
window注入就比較好理解了,就是雙方在JS的window下寫入通信變數。其實上面兩種方法也多少會涉及window注入。它是一個對象,常用的有這幾個方法(想用什麼自己寫入)。
『貳』 如何設置WebView支持js的Alert,Confirm,Prompt函數的彈出提示框
默認情況下,Android WebView是不支持js的Alert(),Confirm(),Prompt()函數的彈出提示框的.即使設置了setJavaScriptEnabled(true);也是沒用的.那麼,如何才能讓WebView可以支持js的這3個函數呢.可以通過設置WebChromeClient對象來完成.WebChromeClient主要輔助WebView處理Javascript的對話框、網站圖標、網站title、載入進度等等.
這里主要重寫WebChromeClient的3個方法:
onJsAlert :警告框(WebView上alert無效,需要定製WebChromeClient處理彈出)
onJsPrompt : 提示框.
onJsConfirm : 確定框.
效果圖分別為:
1.Alert
2.Prompt
3.Confirm
先來看看js的頁面代碼:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Document</title>
<script type="text/javascript">
function call(){
var value = document.getElementById("input").value;
alert(value);
}
//警告
function onAlert(){
alert("This is a alert sample from html");
}
//確定
function onConfirm(){
var b = confirm("are you sure to login?");
alert("your choice is "+b);
}
//提示
function onPrompt(){
var b = prompt("please input your password","aaa");
alert("your input is "+b);
}
</script>
</head>
<body>
<input type="text" id="input" value="default"/>
<button onclick=call()>點我彈出Alert</button></br>
<input type="button" value="alert" onclick="onAlert()"/></br>
<input type="button" value="confirm" onclick="onConfirm()"/></br>
<input type="button" value="prompt" onclick="onPrompt()"/></br>
</body>
</html>
Android代碼:
package com.example.chenys.webviewdemo;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.EditText;
import android.widget.TextView;
/**
* Created by mChenys on 2015/11/19.
*/
public class TestAlertActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WebView webView = new WebView(this);
setContentView(webView);
webView.requestFocus();
WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);//啟用支持js
//設置響應js 的Alert()函數
webView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder b = new AlertDialog.Builder(TestAlertActivity.this);
b.setTitle("Alert");
b.setMessage(message);
b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
});
b.setCancelable(false);
b.create().show();
return true;
}
//設置響應js 的Confirm()函數
@Override
public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder b = new AlertDialog.Builder(TestAlertActivity.this);
b.setTitle("Confirm");
b.setMessage(message);
b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
});
b.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.cancel();
}
});
b.create().show();
return true;
}
//設置響應js 的Prompt()函數
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, final JsPromptResult result) {
final View v = View.inflate(TestAlertActivity.this, R.layout.prompt_dialog, null);
((TextView) v.findViewById(R.id.prompt_message_text)).setText(message);
((EditText) v.findViewById(R.id.prompt_input_field)).setText(defaultValue);
AlertDialog.Builder b = new AlertDialog.Builder(TestAlertActivity.this);
b.setTitle("Prompt");
b.setView(v);
b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String value = ((EditText) v.findViewById(R.id.prompt_input_field)).getText().toString();
result.confirm(value);
}
});
b.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.cancel();
}
});
b.create().show();
return true;
}
});
webView.loadUrl("file:///android_asset/index3.html");
}
}
有2個需要注意的:
1.重寫onJsPrompt 方法,需要我們自定一個提示的布局文件,如下:prompt_dialog.xml
就是一個提示的TextView和輸入文本的EditTex而已.
[html] view plain
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android=""
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/prompt_message_text"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<EditText
android:id="@+id/prompt_input_field"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minWidth="250dp"
android:selectAllOnFocus="true"
android:scrollHorizontally="true"/>
</LinearLayout>
2.WebView需要支持js的話,要記得加啟用js的支持.
WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
『叄』 onjsprompt 浣曟椂璋冪敤
寮虹殑欏甸潰鎴戜滑浼氬嚲鍚戜簬浣跨敤native鏉ュ畬鎴愶紝鑰屼竴鏃︿嬌鐢ㄤ簡h5錛屼負浜嗗湪h5涓灝藉彲鑳界殑寰楀埌native鐨勪綋楠岋紝鎴戜滑native灞傞渶瑕佹毚闇蹭竴浜涙柟娉曠粰js璋冪敤錛屾瘮濡傦紝寮筎oast鎻愰啋錛屽脊Dialog錛屽垎浜絳夌瓑錛屾湁鏃跺欑敋鑷蟲妸h5鐨勭綉緇滆鋒眰鏀劇潃native鍘誨畬鎴愶紝鑰孞SBridge鍋氬緱濂界殑涓涓鍏稿瀷灝辨槸寰淇★紝寰淇$粰寮鍙戣呮彁渚涗簡JSSDK錛岃SDK涓鏆撮湶浜嗗緢澶氬井淇native灞傜殑鏂規硶錛屾瘮濡傛敮浠橈紝瀹氫綅絳夈
閭d箞錛屾庝箞鍘誨疄鐜頒竴涓鍏煎笰ndroid鍚勭増鏈鍙堝叿鏈変竴瀹氬畨鍏ㄦх殑JSBridge鍛錛熸垜浠鐭ラ亾錛屽湪WebView涓錛屽傛灉java瑕佽皟鐢╦s鐨勬柟娉曪紝鏄闈炲父瀹規槗鍋氬埌鐨勶紝浣跨敤WebView.loadUrl(鈥渏avascript:fu
『肆』 webview判斷 js是否有 交互對象
android與js交互有兩種方式,第一種是通過系統提供的@JavascriptInterface註解實現,第二種就是js注入。下面來詳細講解一下二者的使用方式,原理,區別。
一、@JavascriptInterface實現
實現步驟:
a.設置WebView支持js腳本
b.為提供給js調用的方法加上@JavascriptInterface註解c.給WebView添加js介面
[java] view plain
webView.getSettings().setJavaScriptEnabled(true);webView.addJavascriptInterface(new JSMethod(mContext), "lh");public class JSMethod {
private Context mContext;
public JSMethod(Context mContext) {
this.mContext = mContext;
}
@JavascriptInterface
public void toast(String msg) {
Toast.makeText(mContext, msg == null ? "" : msg, Toast.LENGTH_SHORT).show();}
}
js端調用android方法:
[javascript] view plain
lh.toast("Hello,China!");
android端執行js方法:
[java] view plain
webView.loadUrl("javascript:console(" + "'Hello,China!'" + ")"");二、js注入實現
先來說說原理吧,當js調用prompt()方法時,WebChromeClient.onJsPrompt()方法會被觸發,當js觸發Android提供的介面方法時,將該方法的方法名稱、參數類型、參數值轉成json,然後通過prompt方法傳遞給android端,android端解析json並通過反射執行對應的方法,同時也支持執行匿名回調。
整個流程比較復雜,看圖:
為WebView綁定WebChormeClient監聽,在Html載入進度25%時進行js注入(注入的js是根據android提供給js的對象類名動態生成);動態注入的js代碼如下:
[javascript] view plain
javascript: (function(b) {
console.log("HostApp initialization begin");var a = {
queue: [],
callback: function() {
var d = Array.prototype.slice.call(arguments, 0);//獲取該函數參數並轉換為Array數組var c = d.shift();//取得數組第一個元素
var e = d.shift();
this.queue[c].apply(this, d);//新建一個對象 屬性名稱為取得的c,並將d數組作為他的值。然後將這個對象push到queue數組if(!e) {//e為空的時候,將queue數組屬性名稱為c的對象刪除delete this.queue[c]
}
}
};
//各種賦值,最後都等於同一個函數
a.alert = a.alert = a.alert = a.delayJsCallBack = a.getIMSI = a.getOsSdk = a.goBack = a.overloadMethod = a.overloadMethod = a.passJson2Java = a.passLongType = a.retBackPassJson = a.retJavaObject = a.testLossTime = a.toast = a.toast = function() {var f = Array.prototype.slice.call(arguments, 0);if(f.length < 1) {
throw "HostApp call error, message:miss method name"}
var e = [];
//此段判斷,然後賦值
for(var h = 1; h < f.length; h++) {
var c = f[h];
var j = typeof c;
e[e.length] = j;
if(j == "function") {
var d = a.queue.length;
a.queue[d] = c;
f[h] = d
}
}
//將匿名對象{method: f.shift(),types: e,args: f}轉換成json字元串並用瀏覽器彈出確認可輸入框,然後取得輸入框的值json序列化為js對象var g = JSON.parse(prompt(JSON.stringify({method: f.shift(),
types: e,
args: f
})));
if(g.code != 200) {
throw "HostApp call error, code:" + g.code + ", message:" + g.result}
return g.result
};
//獲取a的屬性值,然後循環
Object.getOwnPropertyNames(a).forEach(function(d) {var c = a[d];
//判斷賦值
if(typeof c === "function" && d !== "callback") {a[d] = function() {
//concat 連接兩個數組
return c.apply(a, [d].concat(Array.prototype.slice.call(arguments, 0)))}
}
});
b.HostApp = a;
console.log("HostApp initialization end")})(window);//閉包函數默認執行,然後賦給window。這樣window.b就可以執行了 b.HostApp就是執行a的內容,但是a具體處理邏輯不對外開放,避免外部污染a內部邏輯代碼不難,可以自行理解,其中回調函數被封裝在了a對象裡面,確保android端可以通過webview.loadUrl()執行回調。
android端回調js代碼如下:
[javascript] view plain
javascript:HostApp.callback(0, 0 ,"call back haha");android提供的每一個js方法都對應一個JsCallback對象,android就可以通過JsCallback對象來生成並執行回調js的代碼。
三、優缺點
a.第一種方式不安全,不添加addJavascriptInterface,甚至默認false,在低於API17的WebView上默認添加"SearchBoxJavaBridge_"到mJavaScriptObjects中。這樣就有可能通過用戶信任的客戶端獲取SD卡的數據;b.第一種方式必須要API大於等於17才能使用
c.第一種方式當有js回調函數需要android端執行時,都需要將匿名回調函數賦值給全局函數才能供android端回調,增加了js和android端通信的封裝層的低效代碼量;而第二種方式則是通過動態注入js的方式則非常方便。
d.第二種方式也有一定限制,比如android提供的方法必須是static修飾的,且方法第一個參數必須為WebView,不過這不影響使用。