Android WebView技術詳解和經驗分享
WebView和JS相互通訊
WebView呼叫JS函式
通用方式,不能獲取JS函式的返回值:
webView.loadUrl("javascript:alert('hello world');");
Android 4.4.及以上系統的WebView專用方式,可以獲取JS函式的返回值:
webView.evaluateJavascript("javascript:add(2,3)", new ValueCallback<String>() { @Override public void onReceiveValue(String value) { Log.i("tag",value);} });
JS呼叫Native函式
首先,需要通過WebView提供的函式註冊JS物件
public class AndroidJSInterface { @JavascriptInterface //注意,所有可被JS呼叫的函式,一定要加上@JavascriptInterface,否則JS無法呼叫 public String handler(String action, String value){ return ""; } }
webView.addJavascriptInterface(new AndroidJSInterface(),"Android");
JS呼叫Native提供的 handler函式的程式碼示例如下:
window.Android.handler("sayHello","hello world");
其中Android物件就是我們通過addJavascriptInterface()函式註冊的JS物件
JS呼叫Native物件,支援有返回值的函式和沒有返回值的函式
JS呼叫Native函式的時候,建議通過JSON資料來傳值,不然的話,可能會出現呼叫失敗的情況
比如:
window.Android.handler("sayHello",JSON.stringify(obj));
當JS不再使用Native物件時,可以把Native物件登出掉
webView.removeJavascriptInterface("Android");
WebView載入URL和HTML字串的方法
WebView載入指定的url
不帶http header
webView.loadUrl(mUrl);
帶http header
Map<String,String> httpHeaders=new LinkedHashMap<>();httpHeaders.put("userName","kgdwbb"); webView.loadUrl(mUrl,httpHeaders);
WebView載入html片段
webView.loadData("<h1>title</h1>","text/html; charset=utf-8", null);
這裡有一個問題,就是當webView.loadData()函式第三個引數傳入頁面的字元編碼的時候,不起作用,可能是這個函式本身的問題
我的解決方法就是在第二個引數裡面傳入頁面的字元編碼,比如charset=utf-8,如果不顯示指定頁面的字元編碼,在顯示中文的時候可能會出現亂碼的情況
WebView 傳送POST請求
程式碼示例:
webView.loadUrl(mUrl); byte[]data="hello world".getBytes(); webView.postUrl(mUrl,data);
WebView Cookie設定
WebView cookie設定一定要在LoadUrl函式之前呼叫,也就是說在WebView傳送網路請求之前設定。
Android通過CookieManager類來設定Cookie,通過CookieSyncManager類把CookieManager類設定的Cookie資料儲存到應用程式/data/data/databases/目錄下的webviewCookiesChromium.db資料庫的cookies表中,這個資料庫屬於全域性公共資料庫,對這個資料庫的操作會影響所有WebView,所以在使用完這個資料庫之後,一定要記得清除設定的Cookie資料,以免對其它使用WebView的APP造成影響。
下面是設定WebViewCookie的程式碼示例:
public static void
synCookies(Context context, String url,Stringcookie) {
CookieManager cookieManager =CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
cookieManager.setCookie(url, cookie);
CookieSyncManager.getInstance().sync();
}
其中cookie是鍵值對型別的字串,比如cookie="userName=kgdwbb"
下面是清除WebViewCookie的程式碼示例:
public static void removeCookie() { CookieManager cookieManager = CookieManager.getInstance(); cookieManager.removeAllCookie(); CookieSyncManager.getInstance().sync(); }
WebView快取設定
設定WebView快取的程式碼示例如下:
WebSettings webSettings=webView.getSettings(); webSettings.setAppCacheEnabled(true); webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
清除WebView快取的程式碼如下:
webView.clearCache(true);
WebView歷史堆疊
程式碼示例如下:
後退
if(webView.canGoBack()){ webView.goBack(); return; }
前進
if(webView.canGoForward()){ webView.goForward(); }
前進或後退
int steps=2; if(webView.canGoBackOrForward(steps)){ webView.goBackOrForward(steps); }
對WebView的堆疊列表進行操作
int backForwardListSize= webView.copyBackForwardList().getSize(); for(int i=0;i<backForwardListSize;i++){ WebHistoryItem item=webView.copyBackForwardList().getItemAtIndex(i); Log.i("tag",item.getUrl()); }
清除WebView歷史堆疊
webView.clearHistory();
WebView啟用檔案下載功能
示例程式碼如下:
webView.setDownloadListener(new DownloadListener() { @Override public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimeType,long contentLength) { //在這裡寫真正的檔案下載的程式碼,WebView是不會自動下載檔案的 } });
onDownloadStart函式引數說明:
url:就是伺服器上檔案的url地址或者檔案流的地址
userAgent:就是使用者瀏覽器的預設userAgent頭
contentDisposition:url對應的檔案型別,當url是伺服器上的一個真實檔案時,這個值為空,當url是伺服器上可以訪問的檔案流時,這個值就會包涵這個檔案流的一些基本資訊,比如檔名等
mimeType:檔案或檔案流的型別,比如二進位制檔案流的mimeType是application/octet-stream
當WebView需要下載網頁裡面的檔案時,會呼叫這個檔案下載介面,我們只需要處理這個介面,就可以實現檔案下載功能。檔案下載功能需要我們自己來實現,WebView預設是不提供檔案下載功能的。
讓WebView支援檔案選擇
WebView預設不支援表單的file標籤,如果想讓WebView支援表單的file標籤,我們可以這樣做:
webView.setWebChromeClient(new WebChromeClient(){ //當WebView需要顯示檔案選擇器時,回撥這個函式,我們可以重寫這個函式,載入我們自定義的檔案選擇器 @Override public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) { return super.onShowFileChooser(webView, filePathCallback, fileChooserParams); } });
自定義WebView的錯誤頁面
當WebView在載入網頁的時候如果出現網路錯誤或者指定的網頁不存在的時候,就會顯示預設的錯誤頁面,如果我們想重寫這個預設的錯誤頁面,可以用下面的方法:
webView.setWebViewClient(new WebViewClient(){ //當WebView發生任何請求錯誤的時候,都會回撥這個函式 @Override public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { super.onReceivedError(view, request, error);
view.loadUrl("file:///android_asset/error.html");//載入自定義的錯誤頁面,前提是這個錯碼頁面一定要存在assets資料夾下面} });
忽略WebView的SSL證書錯誤
當WebView在載入https網頁的時候,如果網頁存在SSL證書錯誤,比如12306網站的證書錯誤,我們可以忽略網頁的錯誤證書,繼續執行下面的網頁,我們可以這樣做:
webView.setWebViewClient(new WebViewClient(){ //當接收到伺服器返回的SSL錯誤時,回撥這個函式 @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { super.onReceivedSslError(view, handler, error); handler.proceed();//忽略SSL證書錯誤,繼續執行 } });
攔截WebView的所有網路請求
要攔截WebView的網路請求,我們可以這樣做:
webView.setWebViewClient(new WebViewClient(){ //當WebView需要進行任何網路請求時,都會呼叫這個函式,我們可以攔截這個函式,做相應的處理 //比如載入快取的圖片等,這個函式已經被Android 4.4以上的系統廢棄,建議在Android 4.4.及以上的系統使用新的函式 @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { return super.shouldInterceptRequest(view, url); } //當WebView需要進行任何網路請求時,都會呼叫這個函式,我們可以攔截這個函式,做相應的處理 //比如載入快取的圖片等,這個函式屬於Android 4.4及以上的WebView提供的新的函式 @Override public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { WebResourceResponse response=super.shouldInterceptRequest(view,request); return response; } });
重寫WebView H5 video標籤的預設屬性
要想讓WebView裡面的所有H5 video標籤都顯示預設的載入進度和視訊的預設圖片,我們可以這樣做:
webView.setWebChromeClient(new WebChromeClient(){ //獲取video載入時預設顯示的第一張圖片 @Override public Bitmap getDefaultVideoPoster() { return super.getDefaultVideoPoster(); } //獲取視訊載入進度View,所以我們可以在這裡重寫預設的video載入進度 @Override public View getVideoLoadingProgressView() { return super.getVideoLoadingProgressView(); } });
捕捉WebView網頁輸出的所有日誌
要監控網頁輸出的日誌,我們可以這樣做:
webView.setWebChromeClient(new WebChromeClient(){ //當JS輸出日誌時,回撥這個函式 @Override public boolean onConsoleMessage(ConsoleMessage consoleMessage) { return super.onConsoleMessage(consoleMessage); } });
WebView JS計時器設定
預設情況下,WebView的計時器在APP進入後臺的時候還是會執行的。如果想讓APP進入後臺的時候暫時WebView的計時器,在APP進入前臺的時候恢復WebView的計時器,程式碼如下:
@Override protected void onPause() { super.onPause(); //當Activity頁面進入後臺的時候,暫停WebView的計時器,這樣WebView頁面的JS計時器就會被暫停 if(webView!=null) webView.pauseTimers(); } @Override protected void onResume() { super.onResume(); //當Activity頁面進入前臺的時候,恢復WebView的計時器,這樣WebView頁面的JS計時器就會被恢復執行 if(webView!=null) webView.resumeTimers(); }
WebView資源釋放
預設情況下,在Activity銷燬的時候,WebView是不會自動釋放它佔用的系統資源的,我們需要手動進行釋放,否則會造成嚴重的記憶體洩露,
下面是釋放WebView佔用記憶體的程式碼:
@Override protected void onDestroy() { //釋放WebView資源,否則會造成記憶體洩露 if(webView!=null){ webView.destroy(); webView=null; } super.onDestroy(); }
WebView常用設定功能彙總
WebView啟用JS支援
WebSettingswebSettings=webView.getSettings();
webSettings.setJavaScriptEnabled(true);
WebView啟用雙指縮放功能
webSettings.setDisplayZoomControls(true);//true顯示縮放控制元件,false隱藏縮放控制元件
webSettings.setBuiltInZoomControls(true);
webSettings.setSupportZoom(true);
WebView啟用https支援
在android 5.0之前,WebView是可以在http環境中直接訪問https資源和服務的,但是在android 5.0及以後,要想在http環境訪問https資源和服務,就需求在WebView中開啟對https的支援,程式碼如下:
WebSettingswebSettings=webView.getSettings();
if(Build.VERSION.SDK_INT>Build.VERSION_CODES.LOLLIPOP){
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
WebView啟用本地儲存功能
預設情況下,是無法使用JS的localStorage,sessionStorage物件訪問本地儲存的,下面是開啟WebView本地儲存功能的程式碼:
WebSettings webSettings=webView.getSettings();
webSettings.setDomStorageEnabled(true);
WebView啟用LBS定位功能
WebSettings webSettings=webView.getSettings();
webSettings.setGeolocationEnabled(true);
WebView除錯設定
這個API只支援Android 4.4及以上的系統,Android 4.4以下的系統預設開啟了WebView的呼叫功能。
程式碼如下:
if(Build.VERSION.SDK_INT>Build.VERSION_CODES.KITKAT){ webView.setWebContentsDebuggingEnabled(true); }
我們可以藉助Chrome瀏覽器來對WebView進行除錯
WebSettings類解析
WebSettings webSettings=webView.getSettings(); //開啟WebView對JS指令碼的支援 webSettings.setJavaScriptEnabled(true); //設定允許在http環境中訪問https資源和服務 if(Build.VERSION.SDK_INT>Build.VERSION_CODES.LOLLIPOP){ webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); } //設定預設的網頁編碼 webSettings.setDefaultTextEncodingName("utf-8"); //設定JS是否可以開啟WebView新視窗 webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //設定WebView是否支援多視窗,為true,需要重寫WebChromeClient#onCreateWindow函式 webSettings.setSupportMultipleWindows(true); //禁止WebView載入網路圖片 webSettings.setLoadsImagesAutomatically(false); webSettings.setBlockNetworkImage(true); //顯示WebView提供的縮放控制元件 webSettings.setDisplayZoomControls(true);//true顯示縮放控制元件,false隱藏縮放控制元件 webSettings.setBuiltInZoomControls(true); webSettings.setSupportZoom(true); //開啟WebView對資料庫的支援 webSettings.setDatabaseEnabled(true); //開啟WebView的Storage功能,這樣JS的localStorage,sessionStorage物件才可以使用 webSettings.setDomStorageEnabled(true); //開啟WebView的LBS功能,這樣JS的geolocation物件才可以使用 webSettings.setGeolocationEnabled(true); //設定WebView是否自動儲存表單資料 webSettings.setSaveFormData(true); //設定WebView的預設userAgent字串 webSettings.setUserAgentString(""); //設定是否開啟WebView的桌面模式,true是桌面模式,false是移動模式 webSettings.setUseWideViewPort(false); //設定WebView的預設字型,可以通過這個函式,改變WebView的預設字型 webSettings.setStandardFontFamily(""); //設定WebView預設字型的大小 webSettings.setDefaultFontSize(20); //設定WebView支援的最小字型大小 webSettings.setMinimumFontSize(12); //設定頁面的文字縮放倍數 webSettings.setTextZoom(2);
WebViewClient類解析
webView.setWebViewClient(new WebViewClient(){ //當WebView需要載入新的url的時候會呼叫這個函式,在這個函式裡面我們不能呼叫任何WebView的LoadUrl函式, //否則,會出現頁面被多次載入,造成JS location.replace函式失效,還有看起來像302問題導致的WebView需要 //多次點選才能返回的BUG @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { return super.shouldOverrideUrlLoading(view, url); } //WebView頁面已經開始載入指定的url @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); } //WebView載入完指定的url @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); Log.i("hello",url); view.loadUrl("javascript:window.android.handler('<head>'+document.getElementsByTagName('html')[0].innerHTML+'</head>');"); } //當WebView需要進行任何網路請求時,都會呼叫這個函式,我們可以攔截這個函式,做相應的處理 //比如載入快取的圖片等,這個函式已經被Android 4.4以上的系統廢棄,建議在Android 4.4.及以上的系統使用新的函式 @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { return super.shouldInterceptRequest(view, url); } //當WebView需要進行任何網路請求時,都會呼叫這個函式,我們可以攔截這個函式,做相應的處理 //比如載入快取的圖片等,這個函式屬於Android 4.4及以上的WebView提供的新的函式 @Override public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { WebResourceResponse response=super.shouldInterceptRequest(view,request); return response; } //當WebView發生任何請求錯誤的時候,都會回撥這個函式 @Override public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { super.onReceivedError(view, request, error); view.loadUrl("file:///android_asset/error.html");//載入自定義的錯誤頁面 } //當WebView接收到伺服器錯誤時,回撥這個函式 @Override public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) { super.onReceivedHttpError(view, request, errorResponse); } //當WebView表單重新提交時,回撥這個函式 @Override public void onFormResubmission(WebView view, Message dontResend, Message resend) { super.onFormResubmission(view, dontResend, resend); } //當WebView需要更新它的url訪問資料庫的時候,回撥這個函式 @Override public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) { super.doUpdateVisitedHistory(view, url, isReload); } //當接收到伺服器返回的SSL錯誤時,回撥這個函式 @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { super.onReceivedSslError(view, handler, error); handler.proceed();//忽略SSL證書錯誤,繼續執行 } //當WebView訪問https伺服器時,需要客戶端提供對應的SSL證書時,回撥這個函式 @Override public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) { super.onReceivedClientCertRequest(view, request); } //當WebView接收到伺服器的授權請求時,回撥這個函式 @Override public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) { super.onReceivedHttpAuthRequest(view, handler, host, realm); } //當發生鍵盤事件時,回撥這個函式 @Override public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) { return super.shouldOverrideKeyEvent(view, event); } //當WebView的縮放值發生改變的時候,回撥