WebView 常用的類
- WebView
- WebSettings
- WebViewClient
- WebChromeClient
- addJavascriptInterface
必要權限
<uses-permission android:name="android.permission.INTERNET" />
一、WebView
(1)創(chuàng)建WebView
WebView webview = new WebView(this);
(2)加載url
- 加載網(wǎng)頁 url地址
webView.loadUrl("http://www.itdecent.cn/")
- 加載本地asset目錄下的網(wǎng)頁
注意:將index.html 放在asset目錄下
webView.loadUrl("file:///android_asset/html/index.html"); //加載本地assert目錄下網(wǎng)頁
- 加載Sdcard上的html
(3) 返回和后退
webview是否可以返回到上一頁面 webView.canGoBack()
webview返回到上一頁面 webView.goBack();
webview是否可以前進 webView.canGoForward()
webview前進 webView.goForward();
二、WebSettings Webview設置類
設置WebView的一些屬性、狀態(tài)等,例如允許使用javascript,允許使用緩存,允許使用內(nèi)置的縮放組件,設置支持IS等
//聲明WebSettings子類
WebSettings webSettings = webView.getSettings();
//如果訪問的頁面中要與Javascript交互,則webview必須設置支持Javascript
webSettings.setJavaScriptEnabled(true);
// 若加載的 html 里有JS 在執(zhí)行動畫等操作,會造成資源浪費(CPU、電量)
// 在 onStop 和 onResume 里分別把 setJavaScriptEnabled() 給設置成 false 和 true 即可
//支持插件
webSettings.setPluginsEnabled(true);
//設置自適應屏幕,兩者合用
webSettings.setUseWideViewPort(true); //將圖片調(diào)整到適合webview的大小
webSettings.setLoadWithOverviewMode(true); // 縮放至屏幕的大小
//縮放操作
webSettings.setSupportZoom(true); //支持縮放,默認為true。是下面那個的前提。
webSettings.setBuiltInZoomControls(true); //設置內(nèi)置的縮放控件。若為false,則該WebView不可縮放
webSettings.setDisplayZoomControls(false); //隱藏原生的縮放控件
//其他細節(jié)操作
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //關閉webview中緩存
webSettings.setAllowFileAccess(true); //設置可以訪問文件
webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通過JS打開新窗口
webSettings.setLoadsImagesAutomatically(true); //支持自動加載圖片
webSettings.setDefaultTextEncodingName("utf-8");//設置編碼格式
三、WebViewClient : 主要負責請求事件和各種通知(例如開始加載、加載完畢之后的動作、加載錯誤、對加載url鏈接的攔截)
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
//頁面開始加載時
super.onPageStarted(view, url, favicon);
}
@Override
public void onPageFinished(WebView view, String url) {
//頁面加載結束時
super.onPageFinished(view, url);
}
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
// 這里進行無網(wǎng)絡或錯誤處理,具體可以根據(jù)errorCode的值進行判斷,
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
/**
* 網(wǎng)頁跳轉:
* 1.在當前的webview跳轉到新連接
* view.loadUrl(url);
* 2.調(diào)用系統(tǒng)瀏覽器跳轉到新網(wǎng)頁
* Intent i = new Intent(Intent.ACTION_VIEW);
* i.setData(Uri.parse(url));
* startActivity(i);
*/
return true;
}
});
四、WebChromeClient
輔助處理 javaScript的各種對話框、網(wǎng)站標題、網(wǎng)站圖標、加載進度等。
webView.setWebChromeClient(new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int newProgress) {
// 獲得網(wǎng)頁的加載進度
super.onProgressChanged(view, newProgress);
}
@Override
public void onReceivedTitle(WebView view, String title) {
// 獲取網(wǎng)頁的title,客戶端可以在這里動態(tài)修改頁面的title
// 另外,當加載錯誤時title為“找不到該網(wǎng)頁”
super.onReceivedTitle(view, title);
}
@Override
public final boolean onJsPrompt(WebView view, String url, String message,
String defaultValue, JsPromptResult result) {
if (view instanceof WebViewSafe) {
if (handleJsInterface(view, url, message, defaultValue, result)) {
return true;
}
}
return super.onJsPrompt(view, url, message, defaultValue, result);
}
});
五、與JS交互-addJavascriptInterface
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(new JSInterface(), "jsInterface");
JSInterface對象:
public class JSInterface {
@JavascriptInterface
public void methodA() { }
@JavascriptInterface
public void methodB(String webMessage) { }
}
六、javaScript 與 Android 代碼交互
1. Android 調(diào)用javascrpit代碼
// 文本名:javascript
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Carson_Ho</title>
// JS代碼
<script>
// Android需要調(diào)用的方法
function callJS(){
alert("Android調(diào)用了JS的callJS方法");
}
</script>
</head>
</html>
- webView.loadUrl();
loadUrl()方法 執(zhí)行javaScript 會重新加載頁面,效率低
mWebView.loadUrl("javascript:callJS()");
- 通過 wevView的evaluateJavascript()
evaluateJavascript()僅支持4.4以上的Android版本,無法向下兼容。但是onReceiveValue回調(diào)中,可以直接返回javaScript的返回值。使用方便,效率高。
// 只需要將第一種方法的loadUrl()換成下面該方法即可
mWebView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//此處為 js 返回的結果
}
});
}
JS 調(diào)用Android代碼
- 通過shouldOverrideUrlLoading回調(diào) 攔截webView的 url,調(diào)用Android代碼。
mWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// 步驟2:根據(jù)協(xié)議的參數(shù),判斷是否是所需要的url
// 一般根據(jù)scheme(協(xié)議格式) & authority(協(xié)議名)判斷(前兩個參數(shù))
//假定傳入進來的 url = "js://webview?arg1=111&arg2=222"(同時也是約定好的需要攔截的)
Uri uri = Uri.parse(url);
// 如果url的協(xié)議 = 預先約定的 js 協(xié)議
// 就解析往下解析參數(shù)
if ( uri.getScheme().equals("js")) {
// 如果 authority = 預先約定協(xié)議里的 webview,即代表都符合約定的協(xié)議
// 所以攔截url,下面JS開始調(diào)用Android需要的方法
if (uri.getAuthority().equals("webview")) {
// 步驟3:
// 執(zhí)行JS所需要調(diào)用的邏輯
System.out.println("js調(diào)用了Android的方法");
// 可以在協(xié)議上帶有參數(shù)并傳遞到Android上
HashMap<String, String> params = new HashMap<>();
Set<String> collection = uri.getQueryParameterNames();
}
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
}
);
}
}
- 通過WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回調(diào) 攔截 JS對話框alert()、confirm()、prompt() 消息,調(diào)用Android代碼。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Carson_Ho</title>
<script>
function clickprompt(){
// 調(diào)用prompt()
var result=prompt("js://demo?arg1=111&arg2=222");
alert("demo " + result);
}
</script>
</head>
<!-- 點擊按鈕則調(diào)用clickprompt() -->
<body>
<button type="button" id="button1" onclick="clickprompt()">點擊調(diào)用Android代碼</button>
</body>
</html>
mWebView.setWebChromeClient(new WebChromeClient() {
// 攔截輸入框(原理同方式2)
// 參數(shù)message:代表promt()的內(nèi)容(不是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開始調(diào)用Android需要的方法
if (uri.getAuthority().equals("webview")) {
//
// 執(zhí)行JS所需要調(diào)用的邏輯
System.out.println("js調(diào)用了Android的方法");
// 可以在協(xié)議上帶有參數(shù)并傳遞到Android上
HashMap<String, String> params = new HashMap<>();
Set<String> collection = uri.getQueryParameterNames();
//參數(shù)result:代表消息框的返回值(輸入值)
result.confirm("js調(diào)用了Android的方法成功啦");
}
return true;
}
return super.onJsPrompt(view, url, message, defaultValue, result);
}
// 通過alert()和confirm()攔截的原理相同,此處不作過多講述
// 攔截JS的警告框
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
return super.onJsAlert(view, url, message, result);
}
// 攔截JS的確認框
@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
return super.onJsConfirm(view, url, message, result);
}
}
);
}
}
- 通過JavaScriptInterface,調(diào)用Android 代碼。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Carson</title>
<script>
function callAndroid(){
// 由于對象映射,所以調(diào)用test對象等于調(diào)用Android映射的對象
test.hello("js調(diào)用了android中的hello方法");
}
</script>
</head>
<body>
//點擊按鈕則調(diào)用callAndroid函數(shù)
<button type="button" id="button1" onclick="callAndroid()"></button>
</body>
</html>
// 繼承自Object類
public class AndroidtoJs extends Object {
// 定義JS需要調(diào)用的方法
// 被JS調(diào)用的方法必須加入@JavascriptInterface注解
@JavascriptInterface
public void hello(String msg) {
System.out.println("JS調(diào)用了Android的hello方法");
}
}
// 設置與Js交互的權限
webSettings.setJavaScriptEnabled(true);
// 通過addJavascriptInterface()將Java對象映射到JS對象
//參數(shù)1:Java對象名
//參數(shù)2:Javascript對象名
mWebView.addJavascriptInterface(new AndroidtoJs(), "test");//AndroidtoJS類對象映射到js的test對象
// 加載JS代碼
// 格式規(guī)定為:file:///android_asset/文件名.html
mWebView.loadUrl("file:///android_asset/javascript.html");
七、 WebView 漏洞
webViewde 的任意代碼執(zhí)行漏洞,主要有三個原因
WebView 中 addJavascriptInterface() 接口。
解決 辦法:
(1)Android 4.2版本之后
Google 在Android 4.2 版本中規(guī)定對被調(diào)用的函數(shù)以 @JavascriptInterface進行注解從而避免漏洞攻擊
(2)Android 4.2版本之前
在Android 4.2版本之前采用攔截prompt()進行漏洞修復。searchBoxJavaBridge_接口引起遠程代碼執(zhí)行漏洞
解決方法:刪除searchBoxJavaBridge_接口
// 通過調(diào)用該方法刪除接口
super.removeJavascriptInterface("searchBoxJavaBridge_");`
- accessibility和 accessibilityTraversal接口引起遠程代碼執(zhí)行漏洞
刪除接口
super.removeJavascriptInterface("accessibility");
super.removeJavascriptInterface("accessibilityTraversal");
參考鏈接:
WebView 常規(guī)用法:
http://www.itdecent.cn/p/3c94ae673e2a
JS與Android交互
http://www.itdecent.cn/p/345f4d8a5cfa
WebView漏洞分析:
http://www.itdecent.cn/p/3a345d27cd42
如何點擊h5中的圖片 觸發(fā) Android原生的圖片查看器:
http://www.itdecent.cn/p/7167ee44cfcd