一、WebView
谷歌提供的系統組件,用來加載和展現html網頁,其采用webkit內核驅動,來實現網頁瀏覽功能。
擁有load() URL和本地html文件。
// 云端 webView.loadUrl("https://www.baidu.com");
// 本地 webView.loadUrl("file:///android_asset/demo.html");
注意:
- loadUrl()必須在主線程中執行。
- 加載在線網頁地址是會用到聯網
permission
權限的,所以需要在AndroidManifest.xml
中寫入下面代碼申請權限:
<uses-permission android:name="android.permission.INTERNET" />
- 打開本地html文件時,是不需要設置
WebViewClient
,對應的asstes
目錄的url為:file:///android_asset/xxxxx
。 - WebView基本設置
如果我們需要設置WebView的屬性,是通過WebView.getSettings()獲取設置WebView的WebSettings對象,然后調用WebSettings中的方法來實現的。
// 是否支持縮放,配合方法setBuiltInZoomControls使用,默認true
setSupportZoom(boolean support)
//是否需要用戶手勢來播放Media,默認true
setMediaPlaybackRequiresUserGesture(boolean require)
是否顯示窗口懸浮的縮放控制,默認true
setDisplayZoomControls(boolean enabled)
是否允許訪問WebView內部文件,默認true
setAllowFileAccess(boolean allow)
是否保存表單數據,默認false
setSaveFormData(boolean save)
// 設置頁面文字縮放百分比,默認100%
setTextZoom(int textZoom)
。。。。。。
二、WebViewClient
WebViewClient主要輔助WebView執行處理各種響應請求事件的,比如:
- onLoadResource(WebView view, String url)
在加載頁面資源時會調用,每一個資源(比如圖片)的加載都會調用一次。
public void onLoadResource(WebView view, String url) {
// TODO Auto-generated method stub
if (DEBUG) {
Log.d(TAG, " onLoadResource ");
}
super.onLoadResource(view, url);
}
- onPageStarted(WebView view, String url, Bitmap favicon)
在頁面加載開始時調用。
public void onPageStarted(WebView view, String url, Bitmap favicon) {
// TODO Auto-generated method stub
if (DEBUG) {
Log.d(TAG, " onPageStarted ");
}
if (url.endsWith(".apk")) {
download(url);//下載處理
}
super.onPageStarted(view, url, favicon);
}
- onPageFinished(WebView view, String url)
在頁面加載結束時調用。 - onReceivedError(WebView view, int errorCode,String description, String failingUrl)
加載錯誤的時候會回調,在其中可做錯誤處理,比如再請求加載一次,或者提示404的錯誤頁面
這里有四個參數:
- WebView view:當前的WebView實例
- int errorCode:錯誤碼
- String description:錯誤描述
- String failingUrl:當前出錯的URL
如加載返回錯誤時,重新加載錯誤頁面:
mWebView.setWebViewClient(new WebViewClient(){
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
mWebView.loadUrl("file:///android_asset/error.html");
}
});
- shouldOverrideUrlLoading(WebView view, String url)
攔截 url 跳轉,在里邊添加點擊鏈接跳轉或者操作
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//攔截某個URL,將其轉換成其它URL可以在這里做
view.loadUrl(url);
return true;
}
在點擊請求的是鏈接是才會調用,重寫此方法返回true表明點擊網頁里面的鏈接還是在當前的webview里跳轉,不跳到瀏覽器那邊。
比如,我們攔截所有包含“blog.csdn.net”的地址,將其替換成”www.baidu.com”:
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.contains("blog.csdn.net")){
view.loadUrl("http://www.baidu.com");
}else {
view.loadUrl(url);
}
return true;
}
或者
mWebView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.contains("blog.csdn.net")){
view.loadUrl("http://www.baidu.com");
}
return false;
}
}
在利用shouldOverrideUrlLoading來攔截URL時,如果return true,則會屏蔽系統默認的顯示URL結果的行為,不需要處理的URL也需要調用loadUrl()來加載進WebVIew,不然就會出現白屏;如果return false,則系統默認的加載URL行為是不會被屏蔽的,所以一般建議大家return false,我們只關心我們關心的攔截內容,對于不攔截的內容,讓系統自己來處理即可。
- shouldInterceptRequest(WebView view, String url)
在每一次請求資源時,都會通過這個函數來回調,比如超鏈接、JS文件、CSS文件、圖片等,也就是說瀏覽器中每一次請求資源時,都會回調回來,無論任何資源!但是必須注意的是shouldInterceptRequest函數是在非UI線程中執行的,在其中不能直接做UI操作,如果需要做UI操作,則需要利用Handler來實現,該函數聲明如下:
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
return null;
}
該函數會在請求資源前調用,我們可以通過返回WebResourceResponse的處理結果來讓WebView直接使用我們的處理結果。如果我們不想處理,則直接返回null,系統會繼續加載該資源。 利用這個特性,我們可以解決一個需求:假如網頁中需要加載本地的圖片,我們就可以通過攔截shouldInterceptRequest,并返回結果即可 。
如下:
mWebView.setWebViewClient(new WebViewClient(){
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
try {
if (url.equals("http://localhost/pic.png")) {
AssetFileDescriptor fileDescriptor = getAssets().openFd("drawable.jpg");
InputStream stream = fileDescriptor.createInputStream();
WebResourceResponse response = new WebResourceResponse("image/png", "UTF-8", stream);
return response;
}
}catch (Exception e){
Log.e(TAG,e.getMessage());
}
return super.shouldInterceptRequest(view, url);
}
});
當發現當前加載資源的url是我們自定義的http://localhost/pic.png
時,就直接將本地的圖片drawable.jpg作為結果返回。
- onReceivedSslError(WebView view, SslErrorHandler handler,SslError error)
HTTPS協議是通過SSL來通信的,所以當使用HTTPS通信的網址(以https://
開頭的網站)出現錯誤時,就會通過onReceivedSslError回調通知過來,它的函數聲明為:
public void onReceivedSslError(WebView view, SslErrorHandler handler, android.net.http.SslError error) {
handler.proceed();
}
- WebView view:當前的WebView實例
- SslErrorHandler handler:當前處理錯誤的Handler,它只有兩個函數SslErrorHandler.proceed()和SslErrorHandler.cancel(),SslErrorHandler.proceed()表示忽略錯誤繼續加載,SslErrorHandler.cancel()表示取消加載。在onReceivedSslError的默認實現中是使用的SslErrorHandler.cancel()來取消加載,所以一旦出來SSL錯誤,HTTPS網站就會被取消加載了,如果想忽略錯誤繼續加載就只有重寫onReceivedSslError,并在其中調用SslErrorHandler.proceed()
-
SslError error:當前的的錯誤對象,SslError包含了當前SSL錯誤的基本所有信息。
如加載SSL出錯的網站((12306是通過Https協議來傳輸的,但是它的SSL證書是有問題的,所以我們就以12306網站為例))
mWebView.setWebViewClient(new WebViewClient(){
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
// 一定要注釋掉!
// super.onReceivedSslError(view, handler, error);
handler.proceed();
Log.e(TAG,"sslError:"+error.toString());
}
});
mWebView.loadUrl("https://www.12306.cn/");
注意:當出現SSL錯誤時,WebView默認是取消加載當前頁面,只有去掉onReceivedSslError的默認操作,然后添加SslErrorHandler.proceed()才能繼續加載出錯頁面當HTTPS傳輸出現SSL錯誤時,錯誤會只通過onReceivedSslError回調傳過來,而不會觸發onReceivedError回調的。
- WebViewClient其余函數
/**
* 在加載頁面資源時會調用,每一個資源(比如圖片)的加載都會調用一次
*/
public void onLoadResource(WebView view, String url)
/**
* (WebView發生改變時調用)
* 可以參考http://www.it1352.com/191180.html的用法
*/
public void onScaleChanged(WebView view, float oldScale, float newScale)
/**
* 重寫此方法才能夠處理在瀏覽器中的按鍵事件。
* 是否讓主程序同步處理Key Event事件,如過濾菜單快捷鍵的Key Event事件。
* 如果返回true,WebView不會處理Key Event,
* 如果返回false,Key Event總是由WebView處理。默認:false
*/
public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event)
/**
* 是否重發POST請求數據,默認不重發。
*/
onFormResubmission(WebView view, Message dontResend, Message resend)
/**
* 更新訪問歷史
*/
doUpdateVisitedHistory(WebView view, String url, boolean isReload)
/**
* 通知主程序輸入事件不是由WebView調用。是否讓主程序處理WebView未處理的Input Event。
* 除了系統按鍵,WebView總是消耗掉輸入事件或shouldOverrideKeyEvent返回true。
* 該方法由event 分發異步調用。注意:如果事件為MotionEvent,則事件的生命周期只存在方法調用過程中,
* 如果WebViewClient想要使用這個Event,則需要復制Event對象。
*/
onUnhandledInputEvent(WebView view, InputEvent event)
/**
* 通知主程序執行了自動登錄請求。
*/
onReceivedLoginRequest(WebView view, String realm, String account, String args)
/**
* 通知主程序:WebView接收HTTP認證請求,主程序可以使用HttpAuthHandler為請求設置WebView響應。默認取消請求。
*/
onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm)
/**
* 通知主程序處理SSL客戶端認證請求。如果需要提供密鑰,主程序負責顯示UI界面。
* 有三個響應方法:proceed(), cancel() 和 ignore()。
* 如果調用proceed()和cancel(),webview將會記住response,
* 對相同的host和port地址不再調用onReceivedClientCertRequest方法。
* 如果調用ignore()方法,webview則不會記住response。該方法在UI線程中執行,
* 在回調期間,連接被掛起。默認cancel(),即無客戶端認證
*/
onReceivedClientCertRequest(WebView view, ClientCertRequest request)
三、WebChromeClient
主要輔助WebView處理Javascript的對話框、網站Logo、網站title、load進度等處理。
- onCloseWindow(WebView window)
通知主程序關閉WebView,并從View中移除,WebCore停止任何的進行中的加載和JS功能。
- onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg)
請求主程序創建一個新的Window,如果主程序接收請求,返回true并創建一個新的WebView來裝載Window,然后添加到View中,發送帶有創建的WebView作為參數的resultMsg的給Target。如果主程序拒絕接收請求,則方法返回false。默認不做任何處理,返回false - onJsAlert(WebView view, String url, String message,JsResult result)
當網頁調用alert()來彈出alert彈出框前回調,用以攔截alert()函數
注意:
- 如果需要使網頁中的confrim()、alert()、prompt()函數生效,需要設置WebChromeClient!
- 在使用onJsAlert來攔截alert對話框時,如果不需要再彈出alert對話框,一定要return true;在return false以后,會依然調用系統的默認處理來彈出對話框的
- 如果我們return true,則需要在處理完成以后調用JsResult.confirm()或者JsResult.cancel()來告訴WebView我們點中哪個按鈕來取消程序對話框。否則再次點擊按鈕將會失敗
- onJsPrompt(WebView view, String url, String message,String defaultValue, JsPromptResult result)
當網頁調用prompt()來彈出prompt彈出框前回調,用以攔截prompt()函數 - onJsConfirm(WebView view, String url, String message,JsResult result)
當網頁調用confirm()來彈出confirm彈出框前回調,用以攔截confirm()函數 - onProgressChanged(WebView view, int newProgress)
通知程序當前頁面加載進度
WebView view:當前WebView實例
int newProgress:當前的加載進度,值從0到100 - onReceivedIcon(WebView view, Bitmap icon)
通知當前頁面網站新圖標 - onReceivedTitle(WebView view, String title)
通知頁面標題變化 - onShowCustomView
WebView只是用來處理一些html的頁面內容,只用WebViewClient就行了,如果需要更豐富的處理效果,比如JS、進度條等,就要用到WebChromeClient。
四、CookieSync
-
CookieManager
CookieManager是用來管理Cookie的,主要來管理cookie相關,提供如下API:
- setAcceptCookie()
- setCookie()
- getCookie(String url);
- removeSessionCookies();
- hasCookies()
- removeAllCookie()
-
CookieSyncManager
CookieSyncManagerl繼承WebSyncManager,來管理同步cookie相關,主要有以下API:
- resetSync()
- stopSync()
- sync()
- syncFromRamToFlash()
- checkInstanceIsAllowed()
實現cookie同步:
CookieManager cookieManager = CookieManager.getInstance();
// 接受服務器
cookie cookieManager.setAcceptCookie(true);
//移除之前的cookie
cookieManager.removeSessionCookie();
// 注入cookies
List<String> cookies = getCookies(customCookies);
for (String cookie : cookies) {
cookieManager.setCookie(uri.getHost(), cookie);
}
// 同步cookie
CookieSyncManager.getInstance().sync();
五、緩存模式
webview緩存模式有5種,具體方式:
- LOAD_CACHE_ONLY: 不使用網絡,只讀取本地緩存數據
- LOAD_DEFAULT: 根據cache-control決定是否從網絡上取數據。
- LOAD_CACHE_NORMAL: API level 17中已經廢棄, 從API level 11開始作用同LOAD_DEFAULT模式
- LOAD_NO_CACHE: 不使用緩存,只從網絡獲取數據.
- LOAD_CACHE_ELSE_NETWORK,只要本地有,無論是否過期,或者no-cache,都使用緩存中的數據。
六、Native與JS相互調用
該模塊是針對JsBridge進行封裝的功能介紹:
- Java調用js代碼:
public void registerHandler(final String handlerName, final JsHandler handler) {
this.mWebView.registerHandler(handlerName, new BridgeHandler() {
public void handler(String data, CallBackFunction function) {
if(handler != null) {
handler.OnHandler(handlerName, data, function);
// data實際就是js返回的jsonString數據,本案例為了統一處理,data統一采用{code,msg ,data} 形式,
}
}
});
}
//先注冊回調js的方法
mProgressBarWebView.registerHandlers(mHandlers, new JsHandler() {
@Override
public void OnHandler(String handlerName, String responseData, CallBackFunction function) {
if (handlerName.equals("callNative")) {
ToastUitl.showShort(responseData);
function.onCallBack("我在XXXX");
} else if (handlerName.equals("callJs")) {
ToastUitl.showShort(responseData);
// 想調用你的方法:
function.onCallBack("好的 這是圖片地址 :xxxxxxx");
} if (handlerName.equals("open")) {
mfunction = function;
pickFile();
}
}
});
// 調用js
mProgressBarWebView.callHandler("callNative", "hello H5, 我是java", new JavaCallHandler() {
@Override
public void OnHandler(String handlerName, String jsResponseData) {
ToastUitl.showShort("h5返回的數據:" + jsResponseData);
}
});
- js調用Native
public void callHandler(final String handlerName, String javaData, final JavaCallHandler handler) {
this.mWebView.callHandler(handlerName, javaData, new CallBackFunction() {
public void onCallBack(String data) {
if(handler != null) {
handler.OnHandler(handlerName, data);
}
}
});
}
// 注冊一個"callNative"函數,用來接收java層的數據
bridge.registerHandler("callNative", function(data, responseCallback) {
document.getElementById("show").innerHTML = ("data from Java: = " + data);
var responseData = "hello java ! 我要你的地址!";
alert('JS say:'+ responseData);
// response層
responseCallback(responseData);
});
function onUrl() {
var data = "我要你一個url";
//call native method
window.WebViewJavascriptBridge.callHandler(
'callJs'
, {'param': data }
, function(responseData) {
alert('Js 收到你的地址:'+ responseData);
}
);
}