WebView全面解析
簡介
WebView是android中一個非常重要的控件领追,它的作用是用來展示一個web頁面。它使用的內(nèi)核是webkit
引擎,4.4版本之后溉贿,直接使用Chrome作為內(nèi)置網(wǎng)頁瀏覽器看疙。
作用
- 顯示和渲染網(wǎng)頁豆拨;
- 可與頁面JavaScript交互,實(shí)現(xiàn)混合開發(fā)能庆。
使用介紹
使用WebView之前施禾,不要忘記在清單文件中聲明訪問網(wǎng)絡(luò)權(quán)限:
<uses-permission android:name="android.permission.INTERNET"/>
1、加載頁面
加載頁面一般有以下幾種形式:
//方式一:加載一個網(wǎng)頁
webView.loadUrl("http://www.baidu.com");
//方式二:加載應(yīng)用資源文件內(nèi)的網(wǎng)頁
webView.loadUrl("file:///android_asset/test.html");
//方式三:加載一段代碼
webView.loadData(String data,String mimeType, String encoding);
其中搁胆,方式一和方式二比較好理解弥搞,方式三可能有些朋友不明白,我在這里解釋一下渠旁。
WebView.loadData()
和WebView.loadDataWithBaseURL()
是表示加載某一段代碼攀例,其中,WebView.loadDataWithBaseURL()
兼容性更好顾腊,適用場景更多粤铭,因此,我著重介紹一下這個方法杂靶。
WebView.loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl))
的五個參數(shù):baseUrl
表示基礎(chǔ)的網(wǎng)頁梆惯,data
表示要加載的內(nèi)容,mimeType
表示加載網(wǎng)頁的類型吗垮,encoding
表示編碼格式垛吗,historyUrl
表示可用歷史記錄,可以為null
值抱既。
舉個例子:
String body = "示例:這里有個img標(biāo)簽职烧,地址是相對路徑<img src='/uploads/allimg/130923/1FP02V7-0.png' />";
webView.loadDataWithBaseURL("http://www.jcodecraeer.com", body, "text/html", "utf-8",null);
加載后的網(wǎng)頁:
加載有Header的網(wǎng)頁:
//加載有Header的網(wǎng)頁:
HashMap<String,String> header = new HashMap<>();
...
webView.loadUrl(url,header);
注意:
如果你們直接用上面介紹的這三種方式來加載網(wǎng)頁,很有可能會彈出系統(tǒng)瀏覽器進(jìn)行網(wǎng)頁訪問防泵,這樣使用體驗(yàn)就會很差蚀之!
解決辦法是在loadUrl()
之前加上這樣一句代碼:
webView.setWebViewClient(new WebViewClient());
這樣就可以了,有的朋友可能會這樣做捷泞,重寫WebViewClient
的shouldOverrideUrlLoading()
方法:
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
webView.loadUrl(url);
return true;
}
});
這個也是可以的足删,但是會有一些問題,這個會在后面介紹锁右。
2失受、WebView的生命周期
WebView的生命周期一般跟隨Activity:
@Override
protected void onResume() {
super.onResume();
//恢復(fù)webview的狀態(tài)(不靠譜)
webView.resumeTimers();
//激活webView的狀態(tài)讶泰,能正常加載網(wǎng)頁
webView.onResume();
}
@Override
protected void onPause() {
super.onPause();
//當(dāng)頁面被失去焦點(diǎn)被切換到后臺不可見狀態(tài),需要執(zhí)行onPause
//通過onPause動作通知內(nèi)核暫停所有的動作拂到,比如DOM的解析痪署、plugin的執(zhí)行、JavaScript執(zhí)行兄旬。
webView.onPause();
//當(dāng)應(yīng)用程序(存在webview)被切換到后臺時狼犯,這個方法不僅僅針對當(dāng)前的webview而是全局的全應(yīng)用程序的webview
//它會暫停所有webview的layout,parsing领铐,javascripttimer悯森。降低CPU功耗。(不靠譜)
webView.pauseTimers();
}
//在關(guān)閉了Activity時绪撵,如果Webview的音樂或視頻瓢姻,還在播放。就必須銷毀Webview
//但是注意:webview調(diào)用destory時,webview仍綁定在Activity上
//這是由于自定義webview構(gòu)建時傳入了該Activity的context對象
//因此需要先從父容器中移除webview,然后再銷毀webview:
ViewGroup parent = findViewById(R.id.container);
parent.removeView(webView);
webView.destroy();
3音诈、WebView的一些常用方法
-
瀏覽器是否可以前進(jìn)/后退
//WebView是否可以后退 boolean canGoBack = webView.canGoBack(); //WebView后退 webView.goBack(); //WebView是否可以前進(jìn) boolean canGoForward = webView.canGoForward(); //WebView前進(jìn) webView.goForward(); //以當(dāng)前的index為起始點(diǎn)前進(jìn)或者后退到歷史記錄中指定的steps //如果steps為負(fù)數(shù)則為后退幻碱,正數(shù)則為前進(jìn) boolean canGoBackOrForward = webView.canGoBackOrForward(step);
注意,點(diǎn)擊系統(tǒng)返回鍵時改艇,是結(jié)束當(dāng)前的Activity收班,而非調(diào)用WebView的goBack()
方法。
-
重新加載網(wǎng)頁和停止加載
webView.reload(); //刷新頁面(當(dāng)前頁面的所有資源都會重新加載) webView.stopLoading(); //停止加載
-
清除瀏覽器緩存
//清除網(wǎng)頁訪問留下的緩存 //由于內(nèi)核緩存是全局的因此這個方法不僅僅針對webview而是針對整個應(yīng)用程序. Webview.clearCache(true); //清除當(dāng)前webview訪問的歷史記錄 //只會webview訪問歷史記錄里的所有記錄除了當(dāng)前訪問記錄 Webview.clearHistory(); //這個api僅僅清除自動完成填充的表單數(shù)據(jù)谒兄,并不會清除WebView存儲到本地的數(shù)據(jù) Webview.clearFormData();
-
獲取WebView高度摔桦、內(nèi)容HTML高度和滾動距離
//獲取當(dāng)前可見區(qū)域的頂端距整個頁面頂端的距離,也就是當(dāng)前內(nèi)容滾動的距離承疲。 webView.getScrollY(); //獲取WebView控件的高度邻耕。 webView.getHeight();webView.getBottom(); //獲取HTML的高度(原始高度,不包括縮放后的高度) webView.getContentHeight();
-
WebView下載文件
/** * 當(dāng)下載文件時打開系統(tǒng)自帶的瀏覽器進(jìn)行下載燕鸽,當(dāng)然也可以對捕獲到的 url 進(jìn)行處理在應(yīng)用內(nèi)下載兄世。 **/ webView.setDownloadListener(new DownloadListener() { @Override public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) { Uri uri = Uri.parse(url); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); } });
4、WebView的常用工具類
4.1 WebSettings:對WebView進(jìn)行配置和管理啊研。
WebSettings webSettings = webView.getSettings();
//如果訪問的頁面中要與Javascript交互御滩,則webview必須設(shè)置支持Javascript
webSettings.setJavaScriptEnabled(true);
//設(shè)置自適應(yīng)屏幕,兩者合用
webSettings.setUseWideViewPort(true); //將圖片調(diào)整到適合webview的大小
webSettings.setLoadWithOverviewMode(true); // 縮放至屏幕的大小
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN); //支持內(nèi)容重新布局
//縮放操作
webSettings.setSupportZoom(true); //支持縮放党远,默認(rèn)為true削解。是下面那個的前提。
webSettings.setBuiltInZoomControls(true); //設(shè)置內(nèi)置的縮放控件沟娱。若為false氛驮,則該WebView不可縮放
webSettings.setDisplayZoomControls(false); //隱藏原生的縮放控件
webSettings.setTextZoom(2);//設(shè)置文本的縮放倍數(shù),默認(rèn)為 100
webSettings.setRenderPriority(WebSettings.RenderPriority.HIGH); //提高渲染的優(yōu)先級
webSettings.setStandardFontFamily("");//設(shè)置 WebView 的字體济似,默認(rèn)字體為 "sans-serif"
webSettings.setDefaultFontSize(20);//設(shè)置 WebView 字體的大小矫废,默認(rèn)大小為 16
webSettings.setMinimumFontSize(12);//設(shè)置 WebView 支持的最小字體大小盏缤,默認(rèn)為 8
// 5.1以上默認(rèn)禁止了https和http混用,以下方式是開啟
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
//其他操作
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //關(guān)閉webview中緩存
webSettings.setAllowFileAccess(true); //設(shè)置可以訪問文件
webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通過JS打開新窗口
webSettings.setLoadsImagesAutomatically(true); //支持自動加載圖片
webSettings.setDefaultTextEncodingName("utf-8");//設(shè)置編碼格式
webSettings.setGeolocationEnabled(true);//允許網(wǎng)頁執(zhí)行定位操作
webSettings.setUserAgentString("Mozilla/5.0 (Windows NT 10.0; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0");//設(shè)置User-Agent
//不允許訪問本地文件(不影響assets和resources資源的加載)
webSettings.setAllowFileAccess(false);
webSettings.setAllowFileAccessFromFileURLs(false);
webSettings.setAllowUniversalAccessFromFileURLs(false);
其中蓖扑,WebView設(shè)置緩存唉铜,當(dāng)加載 html 頁面時,WebView會在/data/data/package/
下生成 database
與 cache
兩個文件夾
請求的URL記錄保存在WebViewCache.db
律杠,而URL的內(nèi)容是保存在WebViewCache
文件夾下:
//緩存模式如下:
//LOAD_CACHE_ONLY: 不使用網(wǎng)絡(luò)打毛,只讀取本地緩存數(shù)據(jù)
//LOAD_DEFAULT: (默認(rèn))根據(jù)cache-control決定是否從網(wǎng)絡(luò)上取數(shù)據(jù)。
//LOAD_NO_CACHE: 不使用緩存俩功,只從網(wǎng)絡(luò)獲取數(shù)據(jù).
//LOAD_CACHE_ELSE_NETWORK,只要本地有碰声,無論是否過期诡蜓,或者no-cache,都使用緩存中的數(shù)據(jù)胰挑。
//優(yōu)先使用緩存:
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
//不使用緩存:
webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE)
還有如下幾種:
//開啟 DOM storage API 功能 較大存儲空間蔓罚,使用簡單
settings.setDomStorageEnabled(true);
//設(shè)置數(shù)據(jù)庫緩存路徑 存儲管理復(fù)雜數(shù)據(jù) 方便對數(shù)據(jù)進(jìn)行增加、刪除瞻颂、修改豺谈、查詢 不推薦使用
settings.setDatabaseEnabled(true);
final String dbPath = context.getApplicationContext().getDir("db", Context.MODE_PRIVATE).getPath();
settings.setDatabasePath(dbPath);
//開啟 Application Caches 功能 方便構(gòu)建離線APP 不推薦使用
settings.setAppCacheEnabled(true);
final String cachePath = context.getApplicationContext().getDir("cache", Context.MODE_PRIVATE).getPath();
settings.setAppCachePath(cachePath);
settings.setAppCacheMaxSize(5 * 1024 * 1024);
4.2 WebViewClient:處理各種通知和請求事件。
WebViewClient
類常用方法:
-
shouldOverrideUrlLoading()
:攔截URL請求贡这,重定向(有2個方法茬末,一個是兼容5.0以下,一個是兼容5.0以上盖矫,保險起見兩個都重寫)丽惭。
無論返回true
還是false
,只要為WebView設(shè)置了WebViewClient
辈双,系統(tǒng)就不會再將url交給第三方的瀏覽器去處理了责掏。q其中返回false
,代表將url交給當(dāng)前WebView加載湃望,也就是正常的加載狀態(tài)换衬;shouldOverrideUrlLoading()
返回true
,代表開發(fā)者已經(jīng)對url進(jìn)行了處理证芭,WebView就不會再對這個url進(jìn)行加載了瞳浦。
另外,使用post的方式加載頁面檩帐,此方法不會被調(diào)用术幔。webView.setWebViewClient(new WebViewClient(){ //重定向URL請求,返回true表示攔截此url湃密,返回false表示不攔截此url诅挑。 @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { //作用1:重定向url if(url.startsWith("weixin://")){ url = url.replace("weixin://","http://"); webView.loadUrl(url); } //作用2:在本頁面的WebView打開四敞,防止外部瀏覽器打開此鏈接 view.loadUrl(url); return true; } });
onPageStarted()onPageFinished()
:頁面加載時和頁面加載完畢時調(diào)用。shouldOverrideKeyEvent()
:重寫此方法才能處理瀏覽器中的按鍵事件拔妥。shouldInterceptRequest()
:頁面每一次請求資源之前都會調(diào)用這個方法(非UI線程調(diào)用)忿危。onLoadResource()
:頁面加載資源時調(diào)用,每加載一個資源(比如圖片)就調(diào)用一次没龙。onReceivedError()
:加載頁面的服務(wù)器出現(xiàn)錯誤(比如404)時回調(diào)铺厨。onReceivedSslError()
:重寫此方法可以讓webview處理https請求。doUpdateVisitedHistory()
:更新歷史記錄硬纤。onFormResubmission()
:應(yīng)用程序重新請求網(wǎng)頁數(shù)據(jù)解滓。onReceivedHttpAuthRequest()
:獲取返回信息授權(quán)請求。onScaleChanged()
:WebView發(fā)生縮放改變時調(diào)用筝家。onUnhandledKeyEvent()
:Key事件未被加載時調(diào)用洼裤。
補(bǔ)充,關(guān)鍵方法調(diào)用流程:
情況一:loadUrl()
無重定向時
onPageStarted->onPageFinished
情況二:loadUrl()
網(wǎng)頁A重定向到B時
onPageStarted->shouldOverrideUrlLoading->onPageStarted->onPageFinished->onPageFinished
情況三:在已加載的頁面中點(diǎn)擊鏈接溪王,加載頁面A(無重定向)
shouldOverrideUrlLoading->onPageStarted->onPageFinished
情況四:在已加載的頁面中點(diǎn)擊鏈接腮鞍,加載頁面A(頁面A重定向至頁面B)
shouldOverrideUrlLoading->onPageStarted->shouldOverrideUrlLoading->onPageStarted->onPageFinished->onPageFinished
情況五:執(zhí)行goBack/goForward/reload
方法
onPageStarted->onPageFinished
情況六:發(fā)生資源加載
shouldInterceptRequest->onLoadResource
4.3 WebChromeClient:輔助 WebView 處理 Javascript 的對話框,網(wǎng)站圖標(biāo)莹菱,網(wǎng)站標(biāo)題等等移国。
WebChromeClient
類常用方法:
onProgressChanged()
:獲得網(wǎng)頁的加載進(jìn)度并顯示。onReceivedTitle()
:獲得網(wǎng)頁的標(biāo)題時回調(diào)道伟。onReceivedIcon()
:獲得網(wǎng)頁的圖標(biāo)時回調(diào)迹缀。onCreateWindow()
:打開新窗口時回調(diào)。onCloseWindow()
:關(guān)閉窗口時回調(diào)蜜徽。-
onJsAlert()
:網(wǎng)頁彈出提示框時觸發(fā)此方法裹芝。@Override public boolean onJsAlert(WebView view, String url, String message, JsResult result) { Toast.makeText(MainActivity.this,"Im alert",Toast.LENGTH_SHORT).show(); //部分機(jī)型只會彈出一次提示框,調(diào)用此方法即可解決此問題娜汁。 result.cancel(); //返回true表示不彈出系統(tǒng)的提示框嫂易,返回false表示彈出 return true; }
-
onJsConfirm()
:網(wǎng)頁彈出確認(rèn)框時觸發(fā)此方法。@Override public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { //confirm表示確認(rèn)掐禁,cancel表示取消怜械。 result.confirm(); //返回true表示不彈出系統(tǒng)的提示框,返回false表示彈出 return true; }
-
onJsPrompt()
:網(wǎng)頁彈出輸入框時觸發(fā)此方法傅事。@Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { //confirm表示確認(rèn)(并返回值)缕允,cancel表示取消。 result.confirm("8848"); //返回true表示不彈出系統(tǒng)的提示框蹭越,返回false表示彈出 return true; }
4.4 Cookie 相關(guān)
WebView可以在需要的時候自動同步cookie障本,只需要獲得CookieManager
的對象將cookie設(shè)置進(jìn)去就可以了。
從服務(wù)器的返回頭中取出 cookie 根據(jù)Http請求的客戶端不同,獲取 cookie 的方式也不同驾霜,請自行獲取案训。
4.4.1 設(shè)置cookie,若兩次設(shè)置相同粪糙,則覆蓋上一次的
/**
* 將cookie設(shè)置到 WebView
* @param url 要加載的 url
* @param cookie 要同步的 cookie
*/
public static void syncCookie(String url,String cookie) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
CookieSyncManager.createInstance(context);
}
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setCookie(url, cookie);//如果沒有特殊需求强霎,這里只需要將session id以"key=value"形式作為cookie即可
}
注意:
- 同步 cookie 要在 WebView 加載 url 之前,否則 WebView 無法獲得相應(yīng)的 cookie蓉冈,也就無法通過驗(yàn)證城舞。
cookie應(yīng)該被及時更新,否則很可能導(dǎo)致WebView拿的是舊的session id和服務(wù)器進(jìn)行通信寞酿。 - cookie應(yīng)該被及時更新家夺,否則很可能導(dǎo)致WebView拿的是舊的session id和服務(wù)器進(jìn)行通信。
- CookieManager會將這個Cookie存入該應(yīng)用程序
data/data/package_name/app_WebView/Cookies.db
4.4.2 獲取cookie
/**
* 獲取指定 url 的cookie
*/
public static String syncCookie(String url) {
CookieManager cookieManager = CookieManager.getInstance();
return cookieManager.getCookie(url);
}
4.4.3 清除cookie
// 這個兩個在 API level 21 被拋棄
CookieManager.getInstance().removeSessionCookie();
CookieManager.getInstance().removeAllCookie();
// 推薦使用這兩個伐弹, level 21 新加的
CookieManager.getInstance().removeSessionCookies();// 移除所有過期 cookie
CookieManager.getInstance().removeAllCookies(); // 移除所有的 cookie
//設(shè)置清除cookie后的回調(diào)方法
private void removeCookie(Context context) {
CookieManager.getInstance().removeAllCookies(new ValueCallback<Boolean>() {
@Override
public void onReceiveValue(Boolean value) {
// 清除結(jié)果
}
});
}
5秦踪、其他使用場景介紹
5.1 長按保存圖片或者撥打電話
一般瀏覽器都有長按保存圖片或者撥打圖片的功能,實(shí)現(xiàn)這個功能和WebView.HitTestResult
這個類有關(guān)掸茅,這個類會將你觸摸網(wǎng)頁的地方的類型和其他信息反饋給你。
WebView.HitTestResult
的常用方法:
-
HitTestResult.getType()
:獲取所選中目標(biāo)的類型柠逞,可以是圖片昧狮,超鏈接,郵件板壮,電話等等逗鸣。 -
HitTestResult.getExtra()
:獲取額外的信息。
類型和意義:
WebView.HitTestResult.UNKNOWN_TYPE //未知類型
WebView.HitTestResult.PHONE_TYPE //電話類型
WebView.HitTestResult.EMAIL_TYPE //電子郵件類型
WebView.HitTestResult.GEO_TYPE //地圖類型
WebView.HitTestResult.SRC_ANCHOR_TYPE //超鏈接類型
WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE //帶有鏈接的圖片類型
WebView.HitTestResult.IMAGE_TYPE //單純的圖片類型
WebView.HitTestResult.EDIT_TEXT_TYPE //選中的文字類型
步驟:
1绰精、給WebView設(shè)置長按監(jiān)聽事件撒璧;
2、獲取WebView長按時的WebView.HitTestResult
的事件類型笨使,如果是圖片卿樱,則做處理。
代碼:
webView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
WebView.HitTestResult result = ((WebView) view).getHitTestResult();
if(result != null){
switch (result.getType()){
case WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE:
String imgUrl = result.getExtra();
...
return true;
...
}
}
return false;
}
});
Android與JavaScript交互
Android與JavaScript的交互依賴于WebView硫椰,具體交互方式可以查看我的另一篇文章:WebView與JavaScript的交互總結(jié)
WebView中常見問題
1繁调、Android5.0 WebView中Http和Https混合問題
1.1 在Android 5.0上 Webview 默認(rèn)不允許加載 Http 與 Https 混合內(nèi)容,解決辦法:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
參數(shù)說明:
-
MIXED_CONTENT_ALWAYS_ALLOW
:允許從任何來源加載內(nèi)容靶草,即使起源是不安全的蹄胰; -
MIXED_CONTENT_NEVER_ALLOW
:不允許Https加載Http的內(nèi)容,即不允許從安全的起源去加載一個不安全的資源奕翔; -
MIXED_CONTENT_COMPATIBILITY_MODE
:當(dāng)涉及到混合式內(nèi)容時裕寨,WebView 會嘗試去兼容最新Web瀏覽器的風(fēng)格。
1.2 設(shè)置WebView接受所有網(wǎng)站的證書
webView.setWebViewClient(new WebViewClient() {
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
// handler.cancel();// Android默認(rèn)的處理方式
handler.proceed();// 接受所有網(wǎng)站的證書
// handleMessage(Message msg);// 進(jìn)行其他處理
}
});
注意:一定要移除super.onReceivedSslError(view, handler, error)
方法,否則不生效宾袜。
2捻艳、避免WebView引起的內(nèi)存泄漏
2.1 WebView所在的Activity新啟一個進(jìn)程
<activity
android:name=".ui.activity.Html5Activity"
android:process=":lyl.boon.process.web">
<intent-filter>
<action android:name="com.lyl.boon.ui.activity.htmlactivity"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
結(jié)束的時候直接System.exit(0)
退出當(dāng)前進(jìn)程。
2.2 動態(tài)創(chuàng)建WebView
在代碼中創(chuàng)建WebView试和,而不是在XML中創(chuàng)建:
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
mWebView = new WebView(getApplicationContext());
mWebView.setLayoutParams(params);
mLayout.addView(mWebView);
2.3 Activity銷毀的時候先移除WebView
@Override
protected void onDestroy() {
if (mWebView != null) {
((ViewGroup) mWebView.getParent()).removeView(mWebView);
mWebView.destroy();
mWebView = null;
}
super.onDestroy();
}
3讯泣、混淆導(dǎo)致JavaScript不可用
在proguard-rules.pro
文件中配置:
-keepattributes *Annotation*
-keepattributes *JavascriptInterface*
-keep public class org.mq.study.webview.DemoJavaScriptInterface{
public <methods>;
}
如果是內(nèi)部類:
-keepattributes *Annotation*
-keepattributes *JavascriptInterface*
-keep public class org.mq.study.webview.webview.DemoJavaScriptInterface$InnerClass{
public <methods>;
}
4、監(jiān)聽WebView網(wǎng)頁加載完畢
WebViewClient
的onPageFinished()
方法不能保證一定是在網(wǎng)頁加載完畢后調(diào)用阅悍,也有可能是一些其他情況下也會調(diào)用好渠,因此,最好用WebChromeClient
的onProgressChanged()
方法來監(jiān)聽:
webView.setWebChromeClient(new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int position) {
progressBar.setProgress(position);
if (position == 100) {
progressBar.setVisibility(View.GONE);
}
super.onProgressChanged(view, position);
}
});
5节视、 HTML5 video 在 WebView 全屏顯示
當(dāng)網(wǎng)頁全屏播放視頻時會調(diào)用WebChromeClient.onShowCustomView()
方法拳锚,所以可以通過將 video 播放的視圖全屏達(dá)到目的。
@Override
public void onShowCustomView(View view, CustomViewCallback callback) {
if (view instanceof FrameLayout && fullScreenView != null) {
// A video wants to be shown
this.videoViewContainer = (FrameLayout) view;
this.videoViewCallback = callback;
fullScreenView.addView(videoViewContainer, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
fullScreenView.setVisibility(View.VISIBLE);
isVideoFullscreen = true;
}
}
@Override
public void onHideCustomView() {
if (isVideoFullscreen && fullScreenView != null) {
// Hide the video view, remove it, and show the non-video view
fullScreenView.setVisibility(View.INVISIBLE);
fullScreenView.removeView(videoViewContainer);
// Call back (only in API level <19, because in API level 19+ with chromium webview it crashes)
if (videoViewCallback != null && !videoViewCallback.getClass().getName().contains(".chromium.")) {
videoViewCallback.onCustomViewHidden();
}
isVideoFullscreen = false;
videoViewContainer = null;
videoViewCallback = null;
}
}
注意:很多的手機(jī)版本在網(wǎng)頁視頻播放時是不會調(diào)用這個方法的寻行,所以這個方法局限性很大霍掺。
6、loadData()
加載中文數(shù)據(jù)出現(xiàn)亂碼
使用loadDataWithBaseURL()
方法代替loadData()
加載數(shù)據(jù)拌蜘,不會出現(xiàn)亂碼問題杆烁。 為loadData()
的mimeType參數(shù)傳入“text/html;charset=UTF-8”
,也可以解決亂碼問題简卧。
7兔魂、外部JavaScript攻擊
由于應(yīng)用中的WebView沒有對file:///
類型的url做限制,可能導(dǎo)致外部攻擊者利用Javascript代碼讀取本地隱私數(shù)據(jù)举娩。
7.1 如果WebView不需要使用file協(xié)議析校,直接禁用所有與file協(xié)議相關(guān)的功能即可(不影響對assets和resources資源的加載)。
webSettings.setAllowFileAccess(false);
webSettings.setAllowFileAccessFromFileURLs(false);
webSettings.setAllowUniversalAccessFromFileURLs(false);
7.2 如果WebView需要使用file協(xié)議铜涉,則應(yīng)該禁用file協(xié)議的Javascript功能智玻。具體方法為:在調(diào)用loadUrl方法前,以及在shouldOverrideUrlLoading方法中判斷url的scheme是否為file芙代。如果是file協(xié)議吊奢,就禁用Javascript,否則啟用Javascript纹烹。
//調(diào)用loadUrl前
if("file".equals(Uri.parse(url).getScheme())){//判斷是否為file協(xié)議
webView.getSettings().setJavaScriptEnabled(false);
}else{
webView.getSettings().setJavaScriptEnabled(true);
}
webView.loadUrl(url);
//WebViewClient中做的操作
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
if("file".equals(request.getUrl().getScheme())){//判斷是否為file協(xié)議
view.getSettings().setJavaScriptEnabled(false);
}else{
view.getSettings().setJavaScriptEnabled(true);
}
return false;
}
8事甜、WebView閃爍
WebView關(guān)閉硬件加速功能即可。
webView.setLayerType(View.LAYER_TYPE_SOFTWARE,null);
9滔韵、縮放引起的應(yīng)用崩潰
setDisplayZoomControls(true)
方法會允許顯示系統(tǒng)縮放按鈕逻谦,這個縮放按鈕會在每次出現(xiàn)后的幾秒內(nèi)逐漸消失。但是在部分系統(tǒng)版本中陪蜻,如果在縮放按鈕消失前退出了Activity邦马,就可能引起應(yīng)用崩潰。
解決辦法:調(diào)用setDisplayZoomControls(false)
方法不顯示系統(tǒng)縮放按鈕,反正使用手勢捏合動作就可以實(shí)現(xiàn)網(wǎng)頁的縮放功能了滋将。如果確實(shí)需要使用縮放按鈕邻悬,就需要在Activity的onDestroy()
方法中隱藏WebView。
10随闽、webView控件padding
不起作用
在一個布局文件中有一個WebView父丰,想使用padding屬性讓左右向內(nèi)留出一些空白,但是padding屬性不起左右掘宪,內(nèi)容照樣貼邊顯示蛾扇,反而移動了右邊滾動條的位置。android的bug魏滚,用一個外圍的layout包含webview镀首,可以有所改進(jìn),但不能完全解決鼠次。其實(shí)正確的做法是在webView的加載的css中增加padding,沒必要為了padding而更改xml布局文件更哄。
一個很不錯的WebViewActivity分享(來源于網(wǎng)絡(luò))
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.KeyEvent;
import android.webkit.GeolocationPermissions;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.lyl.test.R;
public class Html5Activity extends AppCompatActivity {
private String mUrl;
private LinearLayout mLayout;
private WebView mWebView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web);
Bundle bundle = getIntent().getBundleExtra("bundle");
mUrl = bundle.getString("url");
Log.d("Url:", mUrl);
mLayout = (LinearLayout) findViewById(R.id.web_layout);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
mWebView = new WebView(getApplicationContext());
mWebView.setLayoutParams(params);
mLayout.addView(mWebView);
WebSettings mWebSettings = mWebView.getSettings();
mWebSettings.setSupportZoom(true);
mWebSettings.setLoadWithOverviewMode(true);
mWebSettings.setUseWideViewPort(true);
mWebSettings.setDefaultTextEncodingName("utf-8");
mWebSettings.setLoadsImagesAutomatically(true);
//調(diào)用JS方法.安卓版本大于17,加上注解 @JavascriptInterface
mWebSettings.setJavaScriptEnabled(true);
saveData(mWebSettings);
newWin(mWebSettings);
mWebView.setWebChromeClient(webChromeClient);
mWebView.setWebViewClient(webViewClient);
mWebView.loadUrl(mUrl);
}
@Override
public void onPause() {
super.onPause();
webView.onPause();
webView.pauseTimers(); //小心這個!P瓤堋成翩!暫停整個 WebView 所有布局、解析赦役、JS麻敌。
}
@Override
public void onResume() {
super.onResume();
webView.onResume();
webView.resumeTimers();
}
/**
* 多窗口的問題
*/
private void newWin(WebSettings mWebSettings) {
//html中的_bank標(biāo)簽就是新建窗口打開,有時會打不開扩劝,需要加以下
//然后 復(fù)寫 WebChromeClient的onCreateWindow方法
mWebSettings.setSupportMultipleWindows(false);
mWebSettings.setJavaScriptCanOpenWindowsAutomatically(true);
}
/**
* HTML5數(shù)據(jù)存儲
*/
private void saveData(WebSettings mWebSettings) {
//有時候網(wǎng)頁需要自己保存一些關(guān)鍵數(shù)據(jù),Android WebView 需要自己設(shè)置
mWebSettings.setDomStorageEnabled(true);
mWebSettings.setDatabaseEnabled(true);
mWebSettings.setAppCacheEnabled(true);
String appCachePath = getApplicationContext().getCacheDir().getAbsolutePath();
mWebSettings.setAppCachePath(appCachePath);
}
WebViewClient webViewClient = new WebViewClient(){
/**
* 多頁面在同一個WebView中打開,就是不新建activity或者調(diào)用系統(tǒng)瀏覽器打開
*/
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
};
WebChromeClient webChromeClient = new WebChromeClient() {
//=========HTML5定位==========================================================
//需要先加入權(quán)限
//<uses-permission android:name="android.permission.INTERNET"/>
//<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
//<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
@Override
public void onReceivedIcon(WebView view, Bitmap icon) {
super.onReceivedIcon(view, icon);
}
@Override
public void onGeolocationPermissionsHidePrompt() {
super.onGeolocationPermissionsHidePrompt();
}
@Override
public void onGeolocationPermissionsShowPrompt(final String origin, final GeolocationPermissions.Callback callback) {
callback.invoke(origin, true, false);//注意個函數(shù)职辅,第二個參數(shù)就是是否同意定位權(quán)限棒呛,第三個是是否希望內(nèi)核記住
super.onGeolocationPermissionsShowPrompt(origin, callback);
}
//=========HTML5定位==========================================================
//=========多窗口的問題==========================================================
@Override
public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
transport.setWebView(view);
resultMsg.sendToTarget();
return true;
}
//=========多窗口的問題==========================================================
};
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && mWebView.canGoBack()) {
mWebView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mWebView != null) {
mWebView.clearHistory();
((ViewGroup) mWebView.getParent()).removeView(mWebView);
mWebView.loadUrl("about:blank");
mWebView.stopLoading();
mWebView.setWebChromeClient(null);
mWebView.setWebViewClient(null);
mWebView.destroy();
mWebView = null;
}
}
}