Android中WebView的使用指南:

筆墨Android發表於2018-04-14

本人的簡書地址

參考文獻: Carson_Ho的Android:這是一份全面 & 詳細的Webview使用攻略

本文知識點:

  • WebView的介紹
  • WebView的基本使用
  • WebView的一些開發中常用的API
  • WebView中一些常見的案例分析及實現

這裡盜用Carson的一張圖片,如果覺得不妥,及時告知馬上刪除

##1.WebView的介紹

相信做Android的基本上都用過WebView,不論是載入網頁還是處理本地內容,基本上都或多或少的使用過WebView。那麼WebView能為我們帶來什麼呢?其實就相當於一個內建的瀏覽器,我們可以使用它完成一下操作:

1.WebView可以載入網頁 2.WebView可以載入html片段 3.WebView可以載入本地html 4.WebView與原生App進行互動 以上種種都是WebView能幫我們完成的內容,所以現在有很多App就採用混合開發。這樣使得我們使用WebView的場景越來越多,所以這裡總結一下關於WebView的一些內容。

##2.WebView的基本使用

其實如果你只用WebView載入一個網頁的話,還是很好上手的。基本上就三步:

  • 在AndroidManifest註冊網路許可權:這個是必須的,切記切記!!!
 <uses-permission android:name="android.permission.INTERNET" />
複製程式碼
  • 載入相應的網頁資源
webView.loadUrl(url);
複製程式碼

但是這裡雖然載入的是一個url,但是你會發現他會直接蹦到系統自帶的瀏覽器中去。作為強大的開發人員,怎麼處理呢?當然是有辦法的了。。。

  • 設定相應的本APP內開啟的方法;
        webView.setWebViewClient(new WebViewClient() {
            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
                view.loadUrl(String.valueOf(request.getUrl()));
                return true;
            }

            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }
        });
複製程式碼

上面的程式碼有幾點需要說明一下:

  • shouldOverrideUrlLoading(WebView view, WebResourceRequest request) 這個方法是在5.0以上的版本上使用的,而**shouldOverrideUrlLoading(WebView view, String url)**這個方法是在5.0以下使用的,有了這兩個方法,就能在自己的APP中開啟相應的網頁連結了。。。

上面就是WebView的簡單實用,基本上就是展示一個網頁,沒有相應的互動問題。

##3.WebView一些開中常用的API ###載入類:

  • loadUrl(string url) 載入url地址的方法,引數就是一個Url地址;這裡有一個點需要注意一下,這個方法載入的地址可以是
    • 網址 如 -> http://www.baidu.com
    • 手機本地html地址 如-> file:///android_asset/test.html
    • app包中assect目錄下的html檔案 如 -> content://com.android.htmlfileprovider/sdcard/test.html
  • loadData(String data, String mimeType, String encoding) 載入html片段
    • 引數1:html片段
    • 引數2:型別
    • 引數3:編碼格式 ###狀態類
  • onResume() 當webView為活躍狀態的時候回撥此方法(獲取焦點)
  • onPause() 切換到後臺的時候回撥此方法(失去焦點)

其實上面這兩個方法是和生命週期匹配的兩個方法

  • pauseTimers() 全域性範圍切換到後臺降低cpu功耗時回撥的方法
  • resumeTimers() 恢復正常狀態的時候回撥的方法

上面這兩個方法,是為了提高自己APP的效能的方法,主要是在生命週期的方法中呼叫的,也就是說在Activity的onResume()和onPause()中呼叫的(個人感覺啊。。。)

  • destroy() 銷燬WebView的方法(主要是在音視訊的時候,在這裡釋放相應的WebView,這裡也是對應相應生命週期的方法的,但是這裡注意一點,在銷燬前必須先移除WebView,因為WebView會持有相應Activity的上下文引用所以這裡要使用其父佈局呼叫相應的removeView(View view)的方法進行移除)

###操作網頁類

  • canGoBack() 是否可以後退
  • goBack() 回退
  • canGoForward() 是否可以前進
  • goForward() 前進網頁
  • **goBackOrForward(intsteps) ** 前進或者後退指定的位置,正數為前進/負數為後退

這裡面涉及到的主要內容就是操作網頁的前進和後退,主要應用場景是什麼呢?試想一下,有的WebView是嵌到APP中的,一般這樣的網頁都是沒有標題的,標題的互動留給APP處理返回的問題,就會用到上面相應的API了,基本上都是處理相應的返回問題

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if ((keyCode == KEYCODE_BACK) && mWb.canGoBack()) {
            mWb.goBack();
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }
    
    @Override
    public void onBackPressed() {
        if (mWb.canGoBack()) {
            mWb.goBack();
        } else {
            super.onBackPressed();
        }
    }
複製程式碼

上面兩段程式碼是一樣的,但是這裡有一個很重要的問題需要注意一下,很重要啊: 有些網頁存在重定向的時候,那麼canGoBack()返回的會一直是true,我這裡使用的是百度的連結,因為百度重定向了,所以一直返回的是true,如果這裡要是自己公司開發的Url的話,應該不會出現這種情況!這裡提供一種解決辦法

    @Override
    public void goBack() {
        WebBackForwardList mWebBackForwardList = copyBackForwardList();
        // 判斷當前歷史列表是否最頂端,其實canGoBack已經判斷過
        if (mWebBackForwardList.getCurrentIndex() > 0) {
            // 獲取歷史列表
            String historyUrl = mWebBackForwardList.getItemAtIndex(
                    mWebBackForwardList.getCurrentIndex() - 1).getUrl();
            if (historyUrl.equals("https://www.baidu.com/")) {
                //回到首頁了
                Activity activity = (Activity) mContext;
                activity.finish();
            }
        }
        super.goBack();
    }
複製程式碼

上面的程式碼就是拷貝出所有前進的頁面,然後判斷因為這裡重定向了,所以你getUrl()獲取的網址永遠是重定向那個,所以這裡我就想直接把網址寫死的話就能處理了,但是我覺得我的解決辦法比較笨,還請大神們指教!!!

###快取類

  • clearCache(true) 清除WebView產生的所有快取
  • clearHistory() 清除當前WebView訪問的歷史資料
  • clearFormData() 清除表單資料

###WebSettings類

這個類是對WebView進行管理和設定的

  • getSettings() 獲取WebSettings的
  • setJavaScriptEnabled(boolean flag); 是否可以和js進行互動
  • setPluginState(PluginState state) 是否支援外掛,這裡面傳遞的是一個列舉物件
    • WebSettings.PluginState.OFF 不支援
    • WebSettings.PluginState.ON 支援
  • setUseWideViewPort(boolean use) 是否將圖片調整到WebView的大小
  • setLoadWithOverviewMode(boolean overview) 是否將WebView調整到螢幕大小

上面兩個API一起使用!切記,因為只有這樣才能讓介面看上去不那麼醜!

  • **setSupportZoom(boolean support)**支援縮放,預設為true。是下面這個API的前提。

  • setBuiltInZoomControls(boolean enabled) 設定內建的縮放控制元件。若為false,則該WebView不可縮放

  • setDisplayZoomControls(boolean enabled) 隱藏原生的縮放控制元件,這裡可以自己在頁面中實現。

  • setCacheMode(@CacheMode int mode) 設定快取的模式

    • WebSettings.LOAD_DEFAULT 預設的模式 根據cache-control決定是否從網路上取資料。
    • WebSettings.LOAD_CACHE_ELSE_NETWORK 只要本地有,無論是否過期,或者no-cache,都使用快取中的資料
    • WebSettings.LOAD_NO_CACHE 不使用快取
    • WebSettings.LOAD_CACHE_ONLY 不使用網路,只載入快取
  • setAllowFileAccess(boolean allow) 設定可以訪問app中assect中的檔案

  • setJavaScriptCanOpenWindowsAutomatically(boolean flag) 支援通過JS開啟新視窗

  • setLoadsImagesAutomatically(boolean flag) 支援自動載入圖片

  • setDefaultTextEncodingName(String encoding) 設定編碼格式

  • setRenderPriority(RenderPriority priority) 設定渲染的優先順序,這裡傳入的是一個列舉

    • NORMAL 正常
    • HIGH 高
    • LOW 低

###WebViewClient類

處理各種通知和請求事件的控制類

  • shouldOverrideUrlLoading(WebView view, WebResourceRequest request)
  • shouldOverrideUrlLoading(WebView view, String url)

下面這個方法已經過時了,最新的是上面的那個方法,對於相應的url可以通過WebResourceRequest進行獲取。重寫這兩個方法自己處理的話,可以不使用相應的瀏覽器進行開啟,在本APP中開啟,這也是一開始的時候為什麼要重寫這個方法。

  • onPageStarted(WebView view, String url, Bitmap favicon) 載入WebView開始的時候回撥的方法,一般都在這裡新增相應的Loadding,與使用者互動,告訴使用者頁面還沒有載入完成;
  • onPageFinished(WebView view, String url) WebView載入完成的時候回撥的方法和上面那個方法是相呼應的,可以在這裡關閉相應的對話方塊!

這裡有一個問題注意一下,還是重定向的問題。如果這個網頁重定向了,那麼上面這兩個方法會被多次呼叫!

  • onLoadResource(WebView view, String url) 這個方法會在每次載入資源的時候回撥,如果圖片很多的話,會被呼叫多次。後面這個url代表的是相應載入的地址。

  • onReceivedError(WebView view, int errorCode,String description, String failingUrl)

  • onReceivedError(WebView view, WebResourceRequest request, WebResourceError error)

上面這個方法已經過時了,可以根據相應的code進行判斷。後面這個方法主要是通過WebResourceError的錯誤編碼進行相應的判斷,處理相應的錯誤!這個方法的主要作用是載入錯誤的回撥。

  • onReceivedError(WebView view, int errorCode,String description, String failingUrl)
  • onReceivedError(WebView view, WebResourceRequest request, WebResourceError error)

這兩個方法是處理相應的https的網路請求的(webView預設是不處理https請求的,頁面會顯示空白,使用如上面的方法進行處理)

            @Override
            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
                super.onReceivedSslError(view, handler, error);
                handler.proceed();
            }

            // 特別注意:5.1以上預設禁止了https和http混用,以下方式是開啟
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                mWebView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
            }
複製程式碼

###WebChromeClient類

如果你對控制類沒有那麼多要求的話,使用上面的控制類就可以了,但是如果你要求比較高的話,就要用到下面這個控制類了

  • onProgressChanged(WebView view, int newProgress) 這個回撥會實時的返回網頁載入的總進度,最大值是100,可以根據當前值顯示進度。
  • onReceivedTitle(WebView view, String title) 獲取當前載入網頁的標題
  • onJsAlert(WebView view, String url, String message, JsResult result) 如果這裡返回true,那麼js中彈出的警告對話方塊就由客戶端處理;
  • onJsAlert(WebView view, String url, String message, JsResult result) 如果這裡返回true,那麼js中彈出的確認對話方塊就由客戶端處理;
  • **onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) ** 如果這裡返回true,那麼js中彈出的提示對話方塊將由客戶端進行處理。

##4.WebView中一些常見的案例分析及實現 ###WebView顯示進度

由很多APP中由都嵌入WebView,不知道大家仔細看過沒有,有的WebView在載入的時候頂部由一個進度條,提示使用者載入的進度,其實很好實現,這裡就帶大家實現一下:

效果圖

  • 頁面搭建
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.demo.MainActivity">

    <ProgressBar
        android:id="@+id/pb_progress"
        style="@style/Widget.AppCompat.ProgressBar.Horizontal"
        android:layout_width="match_parent"
        android:layout_height="5dp"
        android:background="@drawable/progress_bg" />

    <WebView
        android:id="@+id/wb"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>
複製程式碼

這裡就不講解ProgressBar實現水平進度條了,百度一大堆!!!

  • 程式碼實現
    private java.lang.String mUrl = "https://www.baidu.com/";
    private WebView mWb;
    private ProgressBar progressBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mWb = findViewById(R.id.wb);

        progressBar = findViewById(R.id.pb_progress);
        progressBar.setMax(100);

        setWebView(mWb);
    }

    @SuppressLint("SetJavaScriptEnabled")
    private void setWebView(WebView wb) {
        WebSettings settings = wb.getSettings();
        settings.setJavaScriptEnabled(true);
        settings.setPluginState(WebSettings.PluginState.ON);
        settings.setUseWideViewPort(true);
        settings.setLoadWithOverviewMode(true);
        settings.setSupportZoom(true);

        wb.loadUrl(mUrl);
        wb.setWebViewClient(new WebViewClient() {
            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
                view.loadUrl(String.valueOf(request.getUrl()));
                return true;
            }

            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }
        });

        wb.setWebChromeClient(new WebChromeClient() {
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                super.onProgressChanged(view, newProgress);
                progressBar.setProgress(newProgress);
            }
        });
    }
複製程式碼

主要程式碼就這麼多就能,這裡設定了一些其他的屬性,上面都講解了,這裡就不去說了。。。

###WebView載入錯誤顯示預設頁面

private java.lang.String mUrl = "https://google.com/";
    private WebView mWb;
    private ProgressBar progressBar;
    private java.lang.String mErrorUrl = "file:///android_asset/test.html";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mWb = findViewById(R.id.wb);

        progressBar = findViewById(R.id.pb_progress);
        progressBar.setMax(100);

        setWebView(mWb);
    }

    @SuppressLint("SetJavaScriptEnabled")
    private void setWebView(WebView wb) {
        WebSettings settings = wb.getSettings();
        settings.setJavaScriptEnabled(true);
        settings.setPluginState(WebSettings.PluginState.ON);
        settings.setUseWideViewPort(true);
        settings.setLoadWithOverviewMode(true);
        settings.setSupportZoom(true);

        wb.loadUrl(mUrl);
        wb.setWebViewClient(new WebViewClient() {
            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
                view.loadUrl(String.valueOf(request.getUrl()));
                return true;
            }

            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }

            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            @Override
            public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
                super.onReceivedHttpError(view, request, errorResponse);
                // 這個方法在6.0才出現
                int statusCode = errorResponse.getStatusCode();
                System.out.println("onReceivedHttpError code = " + statusCode);
                if (404 == statusCode || 500 == statusCode) {
                    view.loadUrl("about:blank");// 避免出現預設的錯誤介面
                    view.loadUrl(mErrorUrl);
                }
            }

            @RequiresApi(api = Build.VERSION_CODES.M)
            @Override
            public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
                super.onReceivedError(view, request, error);
                // 斷網或者網路連線超時
                if (error.getErrorCode() == ERROR_HOST_LOOKUP || error.getErrorCode() == ERROR_CONNECT || error.getErrorCode() == ERROR_TIMEOUT) {
                    view.loadUrl("about:blank"); // 避免出現預設的錯誤介面
                    view.loadUrl(mErrorUrl);
                }
            }

            @Override
            public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
                super.onReceivedError(view, errorCode, description, failingUrl);
                // 斷網或者網路連線超時
                if (errorCode == ERROR_HOST_LOOKUP || errorCode == ERROR_CONNECT || errorCode == ERROR_TIMEOUT) {
                    view.loadUrl("about:blank"); // 避免出現預設的錯誤介面
                    view.loadUrl(mErrorUrl);
                }
            }
        });

        wb.setWebChromeClient(new WebChromeClient() {
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                super.onProgressChanged(view, newProgress);
                Log.e("done", "onProgressChanged: " + newProgress);
                progressBar.setProgress(newProgress);
            }

            @Override
            public void onReceivedTitle(WebView view, String title) {
                super.onReceivedTitle(view, title);
                // android 6.0 以下通過title獲取
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
                    if (title.contains("404") || title.contains("500") || title.contains("Error")) {
                        view.loadUrl("about:blank");// 避免出現預設的錯誤介面
                        view.loadUrl(mErrorUrl);
                    }
                }
            }
        });
    }
複製程式碼

因為WebView引起了google的重視,所以版本之間存在很多差異,所以這裡區分了很多版本的問題,上面都有相應的註解!其實就是在斷網或者網路重連的時候新增了相應的錯誤頁面,防止使用者看見那個難看的斷網頁面。增加使用者的體驗!

###WebView與JS互動的實現 這裡先貼一段測試的js程式碼,下面會用到:

<html>
<head>
    <meta charset="utf-8">
    <title>App呼叫js的程式碼</title>

    <script>
    // Android需要呼叫的方法
    function callJS(){
       console.log("你看我在哪出來~~~");
    }
    function callAndroid(){
       // 由於物件對映,所以呼叫test物件等於呼叫Android對映的物件
       test.hello("js呼叫了android中的hello方法");
    }
    </script>
</head>
<body>
<h1>假裝這個一個頁面</h1>
<button type="button" id="button1" onclick="callAndroid()"></button>
</body>
</html>
複製程式碼

1. 原生程式碼呼叫js中的程式碼

這裡可以有兩種實現方案: 這裡注意一點,無論使用那種方案呼叫,前提是一定要有下面的程式碼!!!

        WebSettings settings = mWb.getSettings();
        settings.setJavaScriptEnabled(true);//允許相應js程式碼
複製程式碼
  • 通過loadUrl("javascript:callJS()")呼叫js中的程式碼,但是會重新整理相應的頁面(這裡面callJS是上面function的方法名稱)

  • 通過**evaluateJavascript(String script, ValueCallback resultCallback)**的callBack進行呼叫相應的方法(這個處理效率高,但是這個方法是在API19才可以使用的,所以一般在使用的時候通過判斷版本兩個一起使用!!!)

整體的使用程式碼如下:

        if (Build.VERSION.SDK_INT > 18) {
            mWb.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
                @Override
                public void onReceiveValue(String value) {
                    Log.e("done", "onReceiveValue: " + value);
                }
            });
        } else {
            mWb.loadUrl("javascript:callJS()");
        }
複製程式碼

這樣既解決了效率的問題也解決了版本的問題。

2. 原生程式碼呼叫js中的程式碼

這裡最常用的方式就是通過註釋@JavascriptInterface進行呼叫,但是看了Carson_Ho的文章說這裡有漏洞,但是說4.2就沒有這種漏洞了,所以沒有去研究,感覺現在一般的手機都應該超過4.2了,如果以後需要適配的時候在仔細看一下!!!

  • 步驟1: 建立一個類用於給JS提供相應的方法;
    class JSMothod {
        @JavascriptInterface
        public void hello(String msg) {
            System.out.println("JS呼叫了Android的hello方法");
        }
    }
複製程式碼

這裡有一點需要注意的,就是**@JavascriptInterface**這個註解一定要加上,否則對於4.0一下的手機可能會存在問題!!!切記。。。

  • 步驟2: 新增相應的呼叫方法
addJavascriptInterface(Object object, String name);
複製程式碼

這裡面的兩個引數分別是上面寫的那個類物件和相應js中的方法名;具體的程式碼是這樣的。。。

mWb.addJavascriptInterface(new JSMothod(),"test");
複製程式碼

這樣就能實現js呼叫Android的程式碼了,其實很簡單。


基本上上面的內容就囊括了Android在使用WebView中的一些常見的使用場景,可能還有些內容沒有想到,如果有什麼不明白的,可以在下方給我留言,還希望這些內容可以幫到你!!!

相關文章