1. 程式人生 > >Android WebView技術詳解和經驗分享

Android WebView技術詳解和經驗分享

WebViewJS相互通訊

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載入URLHTML字串的方法

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資料夾下面}
});

忽略WebViewSSL證書錯誤

當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啟用本地儲存功能

預設情況下,是無法使用JSlocalStorage,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的縮放值發生改變的時候,回撥