基本使用
1.WebView設(shè)置
// 獲取當前頁面的
URLpublic String getUrl();
// 獲取當前頁面的原始 URL(重定向后可能當前 url 不 同)
// 就是 http headers 的 Referer 參數(shù)毡们,loadUrl 時為 null
public String getOriginalUrl();
// 獲 取當前頁面的標題
public String getTitle();
// 獲取當前頁面的
faviconpublic Bitmap getFavicon();
// 獲取當前頁面的加載進度
public int getProgress();
// 通知 WebView 內(nèi)核網(wǎng)絡(luò)狀態(tài)
// 用于設(shè)置 JS 屬性`window.navigator.isOnline`和產(chǎn)生 HTML5 事件 `online/offline`
public void setNetworkAvailable(boolean networkUp)
// 設(shè)置初始縮放比例
public void setInitialScale(int scaleInPercent)席揽;
2.WebSettings設(shè)置
WebSettings settings = web.getSettings();
// 存儲(storage)
// 啟用 HTML5 DOM storage API,默認值 false
settings.setDomStorageEnabled(true);
// 啟用 Web SQL Database API党巾,這個設(shè)置會影響同一進程內(nèi) 的所有 WebView话原,默認值 false
// 此 API 已不推薦使用蕾管,參考:https://www.w3.org/TR/webdatabase/
settings.setDatabaseEnabled(true);
// 啟用 Application Caches API,必需設(shè)置有效的緩存路徑才 能生效啡氢,默認值 false
// 此 API 已廢棄状囱,參考: https://developer.mozilla.org/zh-CN/docs/Web/HTML/Using_the_application_cache
settings.setAppCacheEnabled(true);
settings.setAppCachePath(context.getCacheDir().getAbsolutePath());
// 定位(location)
settings.setGeolocationEnabled(true);
// 是否保存表單數(shù)據(jù)
settings.setSaveFormData(true);
// 是否當webview調(diào)用requestFocus時為頁面的某個元素設(shè)置焦點, 默認值 true
settings.setNeedInitialFocus(true);
// 是否支持 viewport 屬性倘是,默認值 false// 頁面通過`<meta name="viewport" ... />`自適應手機屏幕
settings.setUseWideViewPort(true);
// 是否使用 overviewmode 加載頁面亭枷,默認值 false// 當頁面寬 度大于 WebView 寬度時,縮小使頁面寬度等于 WebView 寬度
settings.setLoadWithOverviewMode(true);
// 布局算法
settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
// 是否支持 Javascript搀崭,默認值 false
settings.setJavaScriptEnabled(true);
// 是否支持多窗口叨粘,默認值 false
settings.setSupportMultipleWindows(false);
// 是否可用 Javascript(window.open)打開窗口猾编,默認 值 false
settings.setJavaScriptCanOpenWindowsAutomatically(false);
// 資源訪問
settings.setAllowContentAccess(true);
// 是否可訪問 Content Provider 的資源,默認值 true
settings.setAllowFileAccess(true);
// 是否可訪問本地文件升敲,默認值 true
// 是否允許通過 file url 加載的 Javascript 讀取本地文件答倡,默認值 false
settings.setAllowFileAccessFromFileURLs(false);
// 是否允許通過 file url 加載的 Javascript 讀取全部資源(包括文件,http,https),默認值 false
settings.setAllowUniversalAccessFromFileURLs(false);
// 資源加載
settings.setLoadsImagesAutomatically(true);
// 是否自動加載圖片
settings.setBlockNetworkImage(false);
// 禁止加載網(wǎng)絡(luò)圖片
settings.setBlockNetworkLoads(false);
// 禁止加載所有網(wǎng)絡(luò)資源
// 縮放(zoom)
settings.setSupportZoom(true);
// 是否支持縮放
settings.setBuiltInZoomControls(false);
// 是否使用內(nèi)置縮放機制
settings.setDisplayZoomControls(true);
// 是否顯示內(nèi)置縮放控件
// 默認文本編碼驴党,默認值 "UTF-8"
settings.setDefaultTextEncodingName("UTF-8");
settings.setDefaultFontSize(16);
// 默認文字尺寸瘪撇,默認值 16,取值范圍 1-72
settings.setDefaultFixedFontSize(16);
// 默認等寬字體尺寸港庄,默認值 16
settings.setMinimumFontSize(8);
// 最小文字尺寸倔既,默認值 8
settings.setMinimumLogicalFontSize(8);
// 最小文字邏輯尺寸,默認值 8
settings.setTextZoom(100);
// 文字縮放百分比鹏氧,默認值 100
// 字體
settings.setStandardFontFamily("sans-serif");
// 標準字體叉存,默認值 "sans-serif"
settings.setSerifFontFamily("serif");
// 襯線字體,默認值 "serif"
settings.setSansSerifFontFamily("sans-serif");
// 無襯線字體度帮,默認值 "sans-serif"
settings.setFixedFontFamily("monospace");
// 等寬字體歼捏,默認值 "monospace"
settings.setCursiveFontFamily("cursive");
// 手寫體(草書),默認值 "cursive"
settings.setFantasyFontFamily("fantasy");
// 幻想體笨篷,默認值 "fantasy"
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// 用戶是否需要通過手勢播放媒體(不會自動播放)瞳秽,默認值 true
settings.setMediaPlaybackRequiresUserGesture(true);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// 5.0 以上允許加載 http 和 https 混合的頁面(5.0 以下默認允許,5.0+默認禁止)
settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// 是否在離開屏幕時光柵化(會增加內(nèi)存消耗)率翅,默認值 false
settings.setOffscreenPreRaster(false);
}
if (isNetworkConnected(context)) {
// 根據(jù) cache-control 決定是否從網(wǎng)絡(luò)上取數(shù)據(jù)
settings.setCacheMode(WebSettings.LOAD_DEFAULT);
} else {
// 沒網(wǎng)练俐,離線加載,優(yōu)先加載緩存(即使已經(jīng)過期)
settings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
}
// deprecated
settings.setRenderPriority(WebSettings.RenderPriority.HIGH);
settings.setDatabasePath(context.getDir("database", Context.MODE_PRIVATE).getPath());
settings.setGeolocationDatabasePath(context.getFilesDir().getPath());
3.WebViewClient設(shè)置
// 攔截頁面加載冕臭,返回 true 表示宿主 app 攔截并處理了該 url腺晾,否則返回 false 由當前 WebView 處理
// 此 方法在 API24 被廢棄,不處理 POST 請求
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
// 攔截頁面加載辜贵,返回 true 表示宿主 app 攔截并處理了該 url悯蝉,否則返回 false 由當前 WebView 處理
// 此 方法添加于 API24,不處理 POST 請求托慨,可攔截處理子 frame 的非 http 請求
@TargetApi(Build.VERSION_CODES.N)
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
return shouldOverrideUrlLoading(view, request.getUrl().toString());
}
// 此方法廢棄于 API21鼻由,調(diào)用于非 UI 線程
// 攔截資源請求并返回響應數(shù)據(jù),返回 null 時 WebView 將繼續(xù)加 載資源
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
return null;
}
// 此方法添加于 API21厚棵,調(diào)用于非 UI 線程
// 攔截資源請求并返回數(shù)據(jù)蕉世,返回 null 時 WebView 將繼續(xù)加載資 源
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
return shouldInterceptRequest(view, request.getUrl().toString());
}
// 頁面(url)開始加載
public void onPageStarted(WebView view, String url, Bitmap favicon) {}
// 頁面(url)完成加載
public void onPageFinished(WebView view, String url) {}
// 將要加載資源(url)
public void onLoadResource(WebView view, String url) {}
// 這個回調(diào)添加于 API23,僅用于主框架的導航
// 通知應用導航到之前頁面時婆硬,其遺留的 WebView 內(nèi)容將不 再被繪制狠轻。
// 這個回調(diào)可以用來決定哪些 WebView 可見內(nèi)容能被安全地回收,以確保不顯示陳舊的內(nèi)容
// 它 最早被調(diào)用彬犯,以此保證 WebView.onDraw 不會繪制任何之前頁面的內(nèi)容向楼,隨后繪制背景色或需要加載的新內(nèi)容查吊。
// 當 HTTP 響應 body 已經(jīng)開始加載并體現(xiàn)在 DOM 上將在隨后的繪制中可見時,這個方法會被調(diào)用蜜自。
// 這個回 調(diào)發(fā)生在文檔加載的早期菩貌,因此它的資源(css,和圖像)可能不可用卢佣。
// 如果需要更細粒度的視圖更新重荠,查看 postVisualStateCallback(long, WebView.VisualStateCallback).
// 請注意這上邊的所有條件也支持
postVisualStateCallback(long, WebView.VisualStateCallback)
public void onPageCommitVisible(WebView view, String url) {}
// 此方法廢棄于 API23
// 主框架加載資源時出錯
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {}
// 此方法添加于 API23
// 加載資源時出錯,通常意味著連接不到服務(wù)器
// 由于所有資源加載錯誤都會調(diào)用此 方法虚茶,所以此方法應盡量邏輯簡單
@TargetApi(Build.VERSION_CODES.M)
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
if (request.isForMainFrame()) {
onReceivedError(view, error.getErrorCode(), error.getDescription().toString(), request.getUrl().toString());
}
}
// 此方法添加于 API23
// 在加載資源(iframe,image,js,css,ajax...)時收到了 HTTP 錯誤(狀態(tài) 碼>=400)
public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {}
// 是否重新提交表單戈鲁,默認不重發(fā)
public voidonFormResubmission(WebView view, Message dontResend, Message resend) {
dontResend.sendToTarget();
}
// 通知應用可以將當前的 url 存儲在數(shù)據(jù)庫中,意味著當前的訪問 url 已經(jīng)生效并被記錄在內(nèi)核當中嘹叫。
// 此 方法在網(wǎng)頁加載過程中只會被調(diào)用一次婆殿,網(wǎng)頁前進后退并不會回調(diào)這個函數(shù)。
public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) {}
// 加載資源時發(fā)生了一個 SSL 錯誤罩扇,應用必需響應(繼續(xù)請求或取消請求)
// 處理決策可能被緩存用于后續(xù)的 請求婆芦,默認行為是取消請求
public void onReceivedSslError(WebViewview, SslErrorHandlerhandler, SslError error) {
handler.cancel();
}
// 此方法添加于 API21,在 UI 線程被調(diào)用
// 處理 SSL 客戶端證書請求喂饥,必要的話可顯示一個 UI 來提供 KEY消约。
// 有三種響應方式:proceed()/cancel()/ignore(),默認行為是取消請求
// 如果調(diào)用 proceed()或 cancel()员帮,Webview 將在內(nèi)存中保存響應結(jié)果且對相同的"host:port"不會再次調(diào)用 onReceivedClientCertRequest
// 多數(shù)情況下或粮,可通過 KeyChain.choosePrivateKeyAlias 啟動一個 Activity 供用戶選擇合適的私鑰
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) {
request.cancel();
}
// 處理 HTTP 認證請求,默認行為是取消請求
public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
handler.cancel();
}
// 通知應用有個已授權(quán)賬號自動登陸了
public void onReceivedLoginRequest(WebView view, String realm, String account, String args) {}
// 給應用一個機會處理按鍵事件
// 如果返回 true捞高,WebView 不處理該事件氯材,否則 WebView 會一直處理,默 認返回 false
public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {
return false;
}
// 處理未被 WebView 消費的按鍵事件
// WebView 總是消費按鍵事件硝岗,除非是系統(tǒng)按鍵或 shouldOverrideKeyEvent 返回 true
// 此方法在按鍵事件分派時被異步調(diào)用
public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
super.onUnhandledKeyEvent(view, event);
}
// 通知應用頁面縮放系數(shù)變化
public void onScaleChanged(WebView view, float oldScale, float newScale) {}
4. WebChromeClient設(shè)置
// 獲得所有訪問歷史項目的列表氢哮,用于鏈接著色。
public void getVisitedHistory(ValueCallback < String[] > callback) {}
// <video /> 控件在未播放時型檀,會展示為一張海報圖命浴,HTML 中可通過它的'poster'屬性來指定。
// 如果未 指定'poster'屬性贱除,則通過此方法提供一個默認的海報圖生闲。
public Bitmap getDefaultVideoPoster() {
return null;
}
// 當全屏的視頻正在緩沖時,此方法返回一個占位視圖(比如旋轉(zhuǎn)的菊花)。
public View getVideoLoadingProgressView() {
return null;
}
// 接收當前頁面的加載進度
public void onProgressChanged(WebView view, int newProgress) {}
// 接收文檔標題
public void onReceivedTitle(WebView view, String title) {}
// 接收圖標(favicon)
public void onReceivedIcon(WebView view, Bitmap icon) {}
// Android 中處理 Touch Icon 的方案
// http://droidyue.com/blog/2015/01/18/deal-with-touch-icon-in-android/index.html
public void onReceivedTouchIconUrl(WebView view, String url, boolean precomposed) {}
// 通知應用當前頁進入了全屏模式,此時應用必須顯示一個包含網(wǎng)頁內(nèi)容的自定義 View
public void onShowCustomView(View view, CustomViewCallback callback) {}
// 通知應用當前頁退出了全屏模式洗出,此時應用必須隱藏之前顯示的自定義 View
public void onHideCustomView() {}
// 顯示一個 alert 對話框
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
return false;
}
// 顯示一個 confirm 對話框
public boolean onJsConfirm(WebViewview, Stringurl, Stringmessage, JsResult result) {
return false;
}
// 顯示一個 prompt 對話框
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
return false;
}
// 顯示一個對話框讓用戶選擇是否離開當前頁面
public boolean onJsBeforeUnload(WebView view, String url, String message, JsResult result) {
return false;
}
// 指定源的網(wǎng)頁內(nèi)容在沒有設(shè)置權(quán)限狀態(tài)下嘗試使用地理位置 API镀娶。
// 從 API24 開始,此方法只為安全的源 (https)調(diào)用变骡,非安全的源會被自動拒絕
public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {}
// 當前一個調(diào)用 onGeolocationPermissionsShowPrompt() 取消時护昧,隱藏相關(guān)的 UI庄拇。
public void onGeolocationPermissionsHidePrompt() {}
// 通知應用打開新窗口
public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
return false;
}
// 通知應用關(guān)閉窗口
public void onCloseWindow(WebView window) {}
// 請求獲取取焦點
public void onRequestFocus(WebView view) {}
// 通知應用網(wǎng)頁內(nèi)容申請訪問指定資源的權(quán)限(該權(quán)限未被授權(quán)或拒 絕)
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void onPermissionRequest(PermissionRequest request) {
request.deny();
}
// 通知應用權(quán)限的申請被取消倍啥,隱藏相關(guān)的 UI禾乘。
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void onPermissionRequestCanceled(PermissionRequest request) {}
// 為'<input type="file" />'顯示文件選擇器,返回 false 使用默認處理
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public boolean onShowFileChooser(WebView webView, ValueCallback < Uri[] > filePathCallback, FileChooserParams fileChooserParams) {
return false;
}
// 接收 JavaScript 控制臺消息
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
return false;
}
Webview 加載優(yōu)化
- 使用本地資源替代
可以將一些資源文件放在本地的 assets 目錄, 然后重寫 WebViewClient
的 shouldInterceptRequest 方法虽缕,對訪問地址進行攔截始藕,當 url 地址命中本地配置的 url 時,使用本地資源替代氮趋,否則就使用網(wǎng)絡(luò)上的資源伍派。
mWebview.setWebViewClient(new WebViewClient() {
// 設(shè)置不用系統(tǒng)瀏覽器打開,
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) { // 如 果命中本地資源, 使用本地資源替代
if (mDataHelper.hasLocalResource(url)) {
WebResourceResponse response = mDataHelper.getReplacedWebResourceResponse(ge tApplicationContext(), url);
if (response != null) {
return response;
}
}
return super.shouldInterceptRequest(view, url);
}
@TargetApi(VERSION_CODES.LOLLIPOP) @Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest reques t) {
String url = request.getUrl().toString();
if (mDataHelper.hasLocalResource(url)) {
WebResourceResponse response = mDataHelper.getReplacedWebResourceResponse(getApplicatio nContext(), url);
if (response != null) {
return response;
}
}
return super.shouldInterceptRequest(view, request);
}
});
- WebView 初始化慢,可以在初始化同時先請求數(shù)據(jù)剩胁,讓后端和網(wǎng)絡(luò)不要閑著诉植;
- 后端處理慢,可以讓服務(wù)器分 trunk 輸出昵观,在后端計算的同時前端也加載網(wǎng)絡(luò)靜態(tài)資源晾腔;
- 腳本執(zhí)行慢,就讓腳本在最后運行啊犬,不阻塞頁面解析灼擂;
- 同時,合理的預加載椒惨、預緩存可以讓加載速度的瓶頸更戌椭痢;
- WebView 初始化慢康谆,就隨時初始化好一個 WebView 待用领斥;
- DNS 和鏈接慢,想辦法復用客戶端使用的域名和鏈接沃暗;
- 腳本執(zhí)行慢月洛,可以把框架代碼拆分出來,在請求頁面之前就執(zhí)行好孽锥。
內(nèi)存泄漏
直接 new WebView 并傳入 application context 代替在 XML 里面聲明以防止 activity 引用被濫用嚼黔,能解決 90+%的 WebView 內(nèi)存泄漏。
vWeb = new WebView(getContext().getApplicationContext());
container.addView(vWeb);
銷毀 WebView
if (vWeb != null) {
vWeb.setWebViewClient(null);
vWeb.setWebChromeClient(null);
vWeb.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
vWeb.clearHistory();
((ViewGroup) vWeb.getParent()).removeView(vWeb);
vWeb.destroy();
vWeb = null;
}