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的縮放值發生改變的時候,回調這個函數
    @Override
    public void onScaleChanged(WebView view, float oldScale, float newScale) {
        super.onScaleChanged(view, oldScale, newScale);
    }
});

WebChromeClient類解析

webView.setWebChromeClient(new WebChromeClient(){
    //當WebView加載進度改變時,回調這個函數
    @Override
    public void onProgressChanged(WebView view, int newProgress) {
        super.onProgressChanged(view, newProgress);
    }

    //當通過JS改變html文檔的標題時,回調這個函數
    @Override
    public void onReceivedTitle(WebView view, String title) {
        super.onReceivedTitle(view, title);
    }

    //當WebView開啓多窗口模式時,需要處理這個函數,這位JS纔可以打開多個WebView窗口
    @Override
    public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
        return super.onCreateWindow(view, isDialog, isUserGesture, resultMsg);
    }

    //當JS關閉指定的WebView窗口時,回調這個函數
    @Override
    public void onCloseWindow(WebView window) {
        super.onCloseWindow(window);
    }

    //當JS調用alert函數時,回調這個函數
    @Override
    public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
        return super.onJsAlert(view, url, message, result);
    }

    //當JS調用confirm函數時,回調這個函數
    @Override
    public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
        return super.onJsConfirm(view, url, message, result);
    }

    //當JS調用prompt函數時,回調這個函數
    @Override
    public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
        return super.onJsPrompt(view, url, message, defaultValue, result);
    }

    //當JS使用geolocation API訪問當前位置時,回調這個函數
    @Override
    public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
        super.onGeolocationPermissionsShowPrompt(origin, callback);
    }

    //當JS取消geolocation API訪問的時候
    @Override
    public void onGeolocationPermissionsHidePrompt() {
        super.onGeolocationPermissionsHidePrompt();
    }

    //當JS輸出日誌時,回調這個函數
    @Override
    public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
        return super.onConsoleMessage(consoleMessage);
    }

    //獲取video加載時默認顯示的第一張圖片
    @Override
    public Bitmap getDefaultVideoPoster() {
        return super.getDefaultVideoPoster();
    }

    //獲取視頻加載進度View,所以我們可以在這裏重寫默認的video加載進度
    @Override
    public View getVideoLoadingProgressView() {
        return super.getVideoLoadingProgressView();
    }

    //當JS獲取訪問歷史記錄時,回調這個函數
    @Override
    public void getVisitedHistory(ValueCallback<String[]> callback) {
        super.getVisitedHistory(callback);
    }

    //當WebView需要顯示文件選擇器時,回調這個函數,我們可以重寫這個函數,加載我們自定義的文件選擇器
    @Override
    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
        return super.onShowFileChooser(webView, filePathCallback, fileChooserParams);
    }
});

WebView常用問題彙總

Android 7.0及以上系統WebView長按崩潰的解決方法

/**
 
* Android 7.0 WebView長按會崩潰,解決長按崩潰的辦法,就是屏蔽掉Android7.0 WebView的長按事件
 */
privatevoid setWebViewLongClickListener(){
   
webView.setOnLongClickListener(new View.OnLongClickListener() {
       
@Override
       
public boolean onLongClick(View v) {
           
if(Build.VERSION.SDK_INT> Build.VERSION_CODES.N){//android 7.0 Nougat
               
return true;
           
}
            return false;
       
}
    });
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章