一锭碳、WebView
谷歌提供的系統(tǒng)組件爹土,用來加載和展現(xiàn)html網(wǎng)頁嘶摊,其采用webkit內(nèi)核驅(qū)動捉超,來實(shí)現(xiàn)網(wǎng)頁瀏覽功能陈惰。
擁有l(wèi)oad() URL和本地html文件壕鹉。
// 云端 webView.loadUrl("https://www.baidu.com");
// 本地 webView.loadUrl("file:///android_asset/demo.html");
注意:
- loadUrl()必須在主線程中執(zhí)行担扑。
- 加載在線網(wǎng)頁地址是會用到聯(lián)網(wǎng)
permission
權(quán)限的雏赦,所以需要在AndroidManifest.xml
中寫入下面代碼申請權(quán)限:
<uses-permission android:name="android.permission.INTERNET" />
- 打開本地html文件時(shí)劫笙,是不需要設(shè)置
WebViewClient
,對應(yīng)的asstes
目錄的url為:file:///android_asset/xxxxx
星岗。 - WebView基本設(shè)置
如果我們需要設(shè)置WebView的屬性填大,是通過WebView.getSettings()獲取設(shè)置WebView的WebSettings對象,然后調(diào)用WebSettings中的方法來實(shí)現(xiàn)的。
// 是否支持縮放俏橘,配合方法setBuiltInZoomControls使用允华,默認(rèn)true
setSupportZoom(boolean support)
//是否需要用戶手勢來播放Media,默認(rèn)true
setMediaPlaybackRequiresUserGesture(boolean require)
是否顯示窗口懸浮的縮放控制,默認(rèn)true
setDisplayZoomControls(boolean enabled)
是否允許訪問WebView內(nèi)部文件靴寂,默認(rèn)true
setAllowFileAccess(boolean allow)
是否保存表單數(shù)據(jù)磷蜀,默認(rèn)false
setSaveFormData(boolean save)
// 設(shè)置頁面文字縮放百分比,默認(rèn)100%
setTextZoom(int textZoom)
百炬。褐隆。。剖踊。庶弃。。
二德澈、WebViewClient
WebViewClient主要輔助WebView執(zhí)行處理各種響應(yīng)請求事件的歇攻,比如:
- onLoadResource(WebView view, String url)
在加載頁面資源時(shí)會調(diào)用,每一個(gè)資源(比如圖片)的加載都會調(diào)用一次圃验。
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)
在頁面加載開始時(shí)調(diào)用掉伏。
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)
在頁面加載結(jié)束時(shí)調(diào)用。 - onReceivedError(WebView view, int errorCode,String description, String failingUrl)
加載錯(cuò)誤的時(shí)候會回調(diào)澳窑,在其中可做錯(cuò)誤處理斧散,比如再請求加載一次,或者提示404的錯(cuò)誤頁面
這里有四個(gè)參數(shù):
- WebView view:當(dāng)前的WebView實(shí)例
- int errorCode:錯(cuò)誤碼
- String description:錯(cuò)誤描述
- String failingUrl:當(dāng)前出錯(cuò)的URL
如加載返回錯(cuò)誤時(shí)摊聋,重新加載錯(cuò)誤頁面:
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 跳轉(zhuǎn),在里邊添加點(diǎn)擊鏈接跳轉(zhuǎn)或者操作
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//攔截某個(gè)URL鸡捐,將其轉(zhuǎn)換成其它URL可以在這里做
view.loadUrl(url);
return true;
}
在點(diǎn)擊請求的是鏈接是才會調(diào)用,重寫此方法返回true表明點(diǎn)擊網(wǎng)頁里面的鏈接還是在當(dāng)前的webview里跳轉(zhuǎn)麻裁,不跳到瀏覽器那邊箍镜。
比如,我們攔截所有包含“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時(shí)色迂,如果return true,則會屏蔽系統(tǒng)默認(rèn)的顯示URL結(jié)果的行為手销,不需要處理的URL也需要調(diào)用loadUrl()來加載進(jìn)WebVIew歇僧,不然就會出現(xiàn)白屏;如果return false锋拖,則系統(tǒng)默認(rèn)的加載URL行為是不會被屏蔽的诈悍,所以一般建議大家return false,我們只關(guān)心我們關(guān)心的攔截內(nèi)容兽埃,對于不攔截的內(nèi)容侥钳,讓系統(tǒng)自己來處理即可。
- shouldInterceptRequest(WebView view, String url)
在每一次請求資源時(shí)柄错,都會通過這個(gè)函數(shù)來回調(diào)舷夺,比如超鏈接苦酱、JS文件、CSS文件冕房、圖片等躏啰,也就是說瀏覽器中每一次請求資源時(shí),都會回調(diào)回來耙册,無論任何資源!但是必須注意的是shouldInterceptRequest函數(shù)是在非UI線程中執(zhí)行的毫捣,在其中不能直接做UI操作详拙,如果需要做UI操作,則需要利用Handler來實(shí)現(xiàn)蔓同,該函數(shù)聲明如下:
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
return null;
}
該函數(shù)會在請求資源前調(diào)用饶辙,我們可以通過返回WebResourceResponse的處理結(jié)果來讓W(xué)ebView直接使用我們的處理結(jié)果。如果我們不想處理斑粱,則直接返回null弃揽,系統(tǒng)會繼續(xù)加載該資源。 利用這個(gè)特性则北,我們可以解決一個(gè)需求:假如網(wǎng)頁中需要加載本地的圖片矿微,我們就可以通過攔截shouldInterceptRequest,并返回結(jié)果即可 尚揣。
如下:
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);
}
});
當(dāng)發(fā)現(xiàn)當(dāng)前加載資源的url是我們自定義的http://localhost/pic.png
時(shí)涌矢,就直接將本地的圖片drawable.jpg作為結(jié)果返回。
- onReceivedSslError(WebView view, SslErrorHandler handler,SslError error)
HTTPS協(xié)議是通過SSL來通信的快骗,所以當(dāng)使用HTTPS通信的網(wǎng)址(以https://
開頭的網(wǎng)站)出現(xiàn)錯(cuò)誤時(shí)娜庇,就會通過onReceivedSslError回調(diào)通知過來,它的函數(shù)聲明為:
public void onReceivedSslError(WebView view, SslErrorHandler handler, android.net.http.SslError error) {
handler.proceed();
}
- WebView view:當(dāng)前的WebView實(shí)例
- SslErrorHandler handler:當(dāng)前處理錯(cuò)誤的Handler方篮,它只有兩個(gè)函數(shù)SslErrorHandler.proceed()和SslErrorHandler.cancel()名秀,SslErrorHandler.proceed()表示忽略錯(cuò)誤繼續(xù)加載,SslErrorHandler.cancel()表示取消加載藕溅。在onReceivedSslError的默認(rèn)實(shí)現(xiàn)中是使用的SslErrorHandler.cancel()來取消加載匕得,所以一旦出來SSL錯(cuò)誤,HTTPS網(wǎng)站就會被取消加載了蜈垮,如果想忽略錯(cuò)誤繼續(xù)加載就只有重寫onReceivedSslError耗跛,并在其中調(diào)用SslErrorHandler.proceed()
-
SslError error:當(dāng)前的的錯(cuò)誤對象,SslError包含了當(dāng)前SSL錯(cuò)誤的基本所有信息攒发。
如加載SSL出錯(cuò)的網(wǎng)站((12306是通過Https協(xié)議來傳輸?shù)牡魉撬腟SL證書是有問題的,所以我們就以12306網(wǎng)站為例))
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/");
注意:當(dāng)出現(xiàn)SSL錯(cuò)誤時(shí)羔砾,WebView默認(rèn)是取消加載當(dāng)前頁面,只有去掉onReceivedSslError的默認(rèn)操作,然后添加SslErrorHandler.proceed()才能繼續(xù)加載出錯(cuò)頁面當(dāng)HTTPS傳輸出現(xiàn)SSL錯(cuò)誤時(shí)姜凄,錯(cuò)誤會只通過onReceivedSslError回調(diào)傳過來政溃,而不會觸發(fā)onReceivedError回調(diào)的。
- WebViewClient其余函數(shù)
/**
* 在加載頁面資源時(shí)會調(diào)用态秧,每一個(gè)資源(比如圖片)的加載都會調(diào)用一次
*/
public void onLoadResource(WebView view, String url)
/**
* (WebView發(fā)生改變時(shí)調(diào)用)
* 可以參考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處理淫半。默認(rèn):false
*/
public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event)
/**
* 是否重發(fā)POST請求數(shù)據(jù),默認(rèn)不重發(fā)匣砖。
*/
onFormResubmission(WebView view, Message dontResend, Message resend)
/**
* 更新訪問歷史
*/
doUpdateVisitedHistory(WebView view, String url, boolean isReload)
/**
* 通知主程序輸入事件不是由WebView調(diào)用科吭。是否讓主程序處理WebView未處理的Input Event。
* 除了系統(tǒng)按鍵猴鲫,WebView總是消耗掉輸入事件或shouldOverrideKeyEvent返回true对人。
* 該方法由event 分發(fā)異步調(diào)用。注意:如果事件為MotionEvent变隔,則事件的生命周期只存在方法調(diào)用過程中规伐,
* 如果WebViewClient想要使用這個(gè)Event,則需要復(fù)制Event對象匣缘。
*/
onUnhandledInputEvent(WebView view, InputEvent event)
/**
* 通知主程序執(zhí)行了自動登錄請求猖闪。
*/
onReceivedLoginRequest(WebView view, String realm, String account, String args)
/**
* 通知主程序:WebView接收HTTP認(rèn)證請求,主程序可以使用HttpAuthHandler為請求設(shè)置WebView響應(yīng)肌厨。默認(rèn)取消請求培慌。
*/
onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm)
/**
* 通知主程序處理SSL客戶端認(rèn)證請求。如果需要提供密鑰柑爸,主程序負(fù)責(zé)顯示UI界面吵护。
* 有三個(gè)響應(yīng)方法:proceed(), cancel() 和 ignore()。
* 如果調(diào)用proceed()和cancel()表鳍,webview將會記住response馅而,
* 對相同的host和port地址不再調(diào)用onReceivedClientCertRequest方法。
* 如果調(diào)用ignore()方法譬圣,webview則不會記住response瓮恭。該方法在UI線程中執(zhí)行,
* 在回調(diào)期間厘熟,連接被掛起屯蹦。默認(rèn)cancel()维哈,即無客戶端認(rèn)證
*/
onReceivedClientCertRequest(WebView view, ClientCertRequest request)
三、WebChromeClient
主要輔助WebView處理Javascript的對話框登澜、網(wǎng)站Logo阔挠、網(wǎng)站title、load進(jìn)度等處理脑蠕。
- onCloseWindow(WebView window)
通知主程序關(guān)閉WebView购撼,并從View中移除,WebCore停止任何的進(jìn)行中的加載和JS功能谴仙。
- onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg)
請求主程序創(chuàng)建一個(gè)新的Window份招,如果主程序接收請求,返回true并創(chuàng)建一個(gè)新的WebView來裝載Window狞甚,然后添加到View中,發(fā)送帶有創(chuàng)建的WebView作為參數(shù)的resultMsg的給Target廓旬。如果主程序拒絕接收請求哼审,則方法返回false。默認(rèn)不做任何處理孕豹,返回false - onJsAlert(WebView view, String url, String message,JsResult result)
當(dāng)網(wǎng)頁調(diào)用alert()來彈出alert彈出框前回調(diào)涩盾,用以攔截alert()函數(shù)
注意:
- 如果需要使網(wǎng)頁中的confrim()、alert()励背、prompt()函數(shù)生效春霍,需要設(shè)置WebChromeClient!
- 在使用onJsAlert來攔截alert對話框時(shí)叶眉,如果不需要再彈出alert對話框址儒,一定要return true;在return false以后,會依然調(diào)用系統(tǒng)的默認(rèn)處理來彈出對話框的
- 如果我們r(jià)eturn true,則需要在處理完成以后調(diào)用JsResult.confirm()或者JsResult.cancel()來告訴WebView我們點(diǎn)中哪個(gè)按鈕來取消程序?qū)υ捒蛐聘怼7駝t再次點(diǎn)擊按鈕將會失敗
- onJsPrompt(WebView view, String url, String message,String defaultValue, JsPromptResult result)
當(dāng)網(wǎng)頁調(diào)用prompt()來彈出prompt彈出框前回調(diào)莲趣,用以攔截prompt()函數(shù) - onJsConfirm(WebView view, String url, String message,JsResult result)
當(dāng)網(wǎng)頁調(diào)用confirm()來彈出confirm彈出框前回調(diào),用以攔截confirm()函數(shù) - onProgressChanged(WebView view, int newProgress)
通知程序當(dāng)前頁面加載進(jìn)度
WebView view:當(dāng)前WebView實(shí)例
int newProgress:當(dāng)前的加載進(jìn)度饱溢,值從0到100 - onReceivedIcon(WebView view, Bitmap icon)
通知當(dāng)前頁面網(wǎng)站新圖標(biāo) - onReceivedTitle(WebView view, String title)
通知頁面標(biāo)題變化 - onShowCustomView
WebView只是用來處理一些html的頁面內(nèi)容喧伞,只用WebViewClient就行了,如果需要更豐富的處理效果绩郎,比如JS潘鲫、進(jìn)度條等,就要用到WebChromeClient肋杖。
四溉仑、CookieSync
-
CookieManager
CookieManager是用來管理Cookie的,主要來管理cookie相關(guān)兽愤,提供如下API:
- setAcceptCookie()
- setCookie()
- getCookie(String url);
- removeSessionCookies();
- hasCookies()
- removeAllCookie()
-
CookieSyncManager
CookieSyncManagerl繼承WebSyncManager彼念,來管理同步cookie相關(guān)挪圾,主要有以下API:
- resetSync()
- stopSync()
- sync()
- syncFromRamToFlash()
- checkInstanceIsAllowed()
實(shí)現(xiàn)cookie同步:
CookieManager cookieManager = CookieManager.getInstance();
// 接受服務(wù)器
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: 不使用網(wǎng)絡(luò)哲思,只讀取本地緩存數(shù)據(jù)
- LOAD_DEFAULT: 根據(jù)cache-control決定是否從網(wǎng)絡(luò)上取數(shù)據(jù)。
- LOAD_CACHE_NORMAL: API level 17中已經(jīng)廢棄, 從API level 11開始作用同LOAD_DEFAULT模式
- LOAD_NO_CACHE: 不使用緩存吩案,只從網(wǎng)絡(luò)獲取數(shù)據(jù).
- LOAD_CACHE_ELSE_NETWORK棚赔,只要本地有,無論是否過期徘郭,或者no-cache靠益,都使用緩存中的數(shù)據(jù)。
六残揉、Native與JS相互調(diào)用
該模塊是針對JsBridge進(jìn)行封裝的功能介紹:
- Java調(diào)用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實(shí)際就是js返回的jsonString數(shù)據(jù)胧后,本案例為了統(tǒng)一處理,data統(tǒng)一采用{code,msg 抱环,data} 形式壳快,
}
}
});
}
//先注冊回調(diào)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);
// 想調(diào)用你的方法:
function.onCallBack("好的 這是圖片地址 :xxxxxxx");
} if (handlerName.equals("open")) {
mfunction = function;
pickFile();
}
}
});
// 調(diào)用js
mProgressBarWebView.callHandler("callNative", "hello H5, 我是java", new JavaCallHandler() {
@Override
public void OnHandler(String handlerName, String jsResponseData) {
ToastUitl.showShort("h5返回的數(shù)據(jù):" + jsResponseData);
}
});
- js調(diào)用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);
}
}
});
}
// 注冊一個(gè)"callNative"函數(shù),用來接收java層的數(shù)據(jù)
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 = "我要你一個(gè)url";
//call native method
window.WebViewJavascriptBridge.callHandler(
'callJs'
, {'param': data }
, function(responseData) {
alert('Js 收到你的地址:'+ responseData);
}
);
}