WebView全面解析

WebView全面解析

簡介

WebView是android中一個非常重要的控件领追,它的作用是用來展示一個web頁面。它使用的內(nèi)核是webkit引擎,4.4版本之后溉贿,直接使用Chrome作為內(nèi)置網(wǎng)頁瀏覽器看疙。

作用

  1. 顯示和渲染網(wǎng)頁豆拨;
  2. 可與頁面JavaScript交互,實(shí)現(xiàn)混合開發(fā)能庆。

使用介紹

使用WebView之前施禾,不要忘記在清單文件中聲明訪問網(wǎng)絡(luò)權(quán)限:

<uses-permission android:name="android.permission.INTERNET"/>

1、加載頁面

加載頁面一般有以下幾種形式:

    //方式一:加載一個網(wǎng)頁
    webView.loadUrl("http://www.baidu.com");

    //方式二:加載應(yīng)用資源文件內(nèi)的網(wǎng)頁
    webView.loadUrl("file:///android_asset/test.html");

    //方式三:加載一段代碼
    webView.loadData(String data,String mimeType, String encoding);

其中搁胆,方式一和方式二比較好理解弥搞,方式三可能有些朋友不明白,我在這里解釋一下渠旁。

WebView.loadData()WebView.loadDataWithBaseURL()是表示加載某一段代碼攀例,其中,WebView.loadDataWithBaseURL()兼容性更好顾腊,適用場景更多粤铭,因此,我著重介紹一下這個方法杂靶。

WebView.loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl))的五個參數(shù):baseUrl表示基礎(chǔ)的網(wǎng)頁梆惯,data表示要加載的內(nèi)容,mimeType表示加載網(wǎng)頁的類型吗垮,encoding表示編碼格式垛吗,historyUrl表示可用歷史記錄,可以為null值抱既。
舉個例子:

String body = "示例:這里有個img標(biāo)簽职烧,地址是相對路徑<img src='/uploads/allimg/130923/1FP02V7-0.png' />";
webView.loadDataWithBaseURL("http://www.jcodecraeer.com", body, "text/html", "utf-8",null);

加載后的網(wǎng)頁:

loadDataWithBaseURL

加載有Header的網(wǎng)頁:

//加載有Header的網(wǎng)頁:
HashMap<String,String> header = new HashMap<>();
...
webView.loadUrl(url,header);

注意:
如果你們直接用上面介紹的這三種方式來加載網(wǎng)頁,很有可能會彈出系統(tǒng)瀏覽器進(jìn)行網(wǎng)頁訪問防泵,這樣使用體驗(yàn)就會很差蚀之!
解決辦法是在loadUrl()之前加上這樣一句代碼:

webView.setWebViewClient(new WebViewClient());

這樣就可以了,有的朋友可能會這樣做捷泞,重寫WebViewClientshouldOverrideUrlLoading()方法:

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

這個也是可以的足删,但是會有一些問題,這個會在后面介紹锁右。

2失受、WebView的生命周期

WebView的生命周期一般跟隨Activity:

@Override
protected void onResume() {
    super.onResume();
    //恢復(fù)webview的狀態(tài)(不靠譜)
    webView.resumeTimers();
    //激活webView的狀態(tài)讶泰,能正常加載網(wǎng)頁
    webView.onResume();
}

@Override
protected void onPause() {
    super.onPause();
    //當(dāng)頁面被失去焦點(diǎn)被切換到后臺不可見狀態(tài),需要執(zhí)行onPause
    //通過onPause動作通知內(nèi)核暫停所有的動作拂到,比如DOM的解析痪署、plugin的執(zhí)行、JavaScript執(zhí)行兄旬。
    webView.onPause();

    //當(dāng)應(yīng)用程序(存在webview)被切換到后臺時狼犯,這個方法不僅僅針對當(dāng)前的webview而是全局的全應(yīng)用程序的webview
    //它會暫停所有webview的layout,parsing领铐,javascripttimer悯森。降低CPU功耗。(不靠譜)
    webView.pauseTimers();
}

//在關(guān)閉了Activity時绪撵,如果Webview的音樂或視頻瓢姻,還在播放。就必須銷毀Webview
//但是注意:webview調(diào)用destory時,webview仍綁定在Activity上
//這是由于自定義webview構(gòu)建時傳入了該Activity的context對象
//因此需要先從父容器中移除webview,然后再銷毀webview:
ViewGroup parent = findViewById(R.id.container);
parent.removeView(webView);
webView.destroy();

3音诈、WebView的一些常用方法

  • 瀏覽器是否可以前進(jìn)/后退

      //WebView是否可以后退
      boolean canGoBack = webView.canGoBack();
      //WebView后退
      webView.goBack();
      //WebView是否可以前進(jìn)
      boolean canGoForward = webView.canGoForward();
      //WebView前進(jìn)
      webView.goForward();
      //以當(dāng)前的index為起始點(diǎn)前進(jìn)或者后退到歷史記錄中指定的steps
      //如果steps為負(fù)數(shù)則為后退幻碱,正數(shù)則為前進(jìn)
      boolean canGoBackOrForward = webView.canGoBackOrForward(step);
    

注意,點(diǎn)擊系統(tǒng)返回鍵時改艇,是結(jié)束當(dāng)前的Activity收班,而非調(diào)用WebView的goBack()方法。

  • 重新加載網(wǎng)頁和停止加載

      webView.reload(); //刷新頁面(當(dāng)前頁面的所有資源都會重新加載)
      webView.stopLoading(); //停止加載
    
  • 清除瀏覽器緩存

      //清除網(wǎng)頁訪問留下的緩存
      //由于內(nèi)核緩存是全局的因此這個方法不僅僅針對webview而是針對整個應(yīng)用程序.
      Webview.clearCache(true);
    
      //清除當(dāng)前webview訪問的歷史記錄
      //只會webview訪問歷史記錄里的所有記錄除了當(dāng)前訪問記錄
      Webview.clearHistory();
    
      //這個api僅僅清除自動完成填充的表單數(shù)據(jù)谒兄,并不會清除WebView存儲到本地的數(shù)據(jù)
      Webview.clearFormData();
    
  • 獲取WebView高度摔桦、內(nèi)容HTML高度和滾動距離

      //獲取當(dāng)前可見區(qū)域的頂端距整個頁面頂端的距離,也就是當(dāng)前內(nèi)容滾動的距離承疲。
      webView.getScrollY();
      //獲取WebView控件的高度邻耕。
      webView.getHeight();webView.getBottom();
      //獲取HTML的高度(原始高度,不包括縮放后的高度)
      webView.getContentHeight();
    
  • WebView下載文件

      /**
      * 當(dāng)下載文件時打開系統(tǒng)自帶的瀏覽器進(jìn)行下載燕鸽,當(dāng)然也可以對捕獲到的 url 進(jìn)行處理在應(yīng)用內(nèi)下載兄世。
      **/
      webView.setDownloadListener(new DownloadListener() {
          @Override
          public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
               Uri uri = Uri.parse(url);
               Intent intent = new Intent(Intent.ACTION_VIEW, uri);
               startActivity(intent);
          } 
      });
    

4、WebView的常用工具類

4.1 WebSettings:對WebView進(jìn)行配置和管理啊研。

    WebSettings webSettings = webView.getSettings();
    //如果訪問的頁面中要與Javascript交互御滩,則webview必須設(shè)置支持Javascript
    webSettings.setJavaScriptEnabled(true);

    //設(shè)置自適應(yīng)屏幕,兩者合用
    webSettings.setUseWideViewPort(true); //將圖片調(diào)整到適合webview的大小
    webSettings.setLoadWithOverviewMode(true); // 縮放至屏幕的大小

    webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN); //支持內(nèi)容重新布局

    //縮放操作
    webSettings.setSupportZoom(true); //支持縮放党远,默認(rèn)為true削解。是下面那個的前提。
    webSettings.setBuiltInZoomControls(true); //設(shè)置內(nèi)置的縮放控件沟娱。若為false氛驮,則該WebView不可縮放
    webSettings.setDisplayZoomControls(false); //隱藏原生的縮放控件
    webSettings.setTextZoom(2);//設(shè)置文本的縮放倍數(shù),默認(rèn)為 100

    webSettings.setRenderPriority(WebSettings.RenderPriority.HIGH);  //提高渲染的優(yōu)先級

    webSettings.setStandardFontFamily("");//設(shè)置 WebView 的字體济似,默認(rèn)字體為 "sans-serif"
    webSettings.setDefaultFontSize(20);//設(shè)置 WebView 字體的大小矫废,默認(rèn)大小為 16
    webSettings.setMinimumFontSize(12);//設(shè)置 WebView 支持的最小字體大小盏缤,默認(rèn)為 8

    // 5.1以上默認(rèn)禁止了https和http混用,以下方式是開啟
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
    }

    //其他操作
    webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //關(guān)閉webview中緩存
    webSettings.setAllowFileAccess(true); //設(shè)置可以訪問文件
    webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通過JS打開新窗口
    webSettings.setLoadsImagesAutomatically(true); //支持自動加載圖片
    webSettings.setDefaultTextEncodingName("utf-8");//設(shè)置編碼格式
    webSettings.setGeolocationEnabled(true);//允許網(wǎng)頁執(zhí)行定位操作
    webSettings.setUserAgentString("Mozilla/5.0 (Windows NT 10.0; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0");//設(shè)置User-Agent


    //不允許訪問本地文件(不影響assets和resources資源的加載)
    webSettings.setAllowFileAccess(false);
    webSettings.setAllowFileAccessFromFileURLs(false);
    webSettings.setAllowUniversalAccessFromFileURLs(false);

其中蓖扑,WebView設(shè)置緩存唉铜,當(dāng)加載 html 頁面時,WebView會在/data/data/package/下生成 databasecache 兩個文件夾
請求的URL記錄保存在WebViewCache.db律杠,而URL的內(nèi)容是保存在WebViewCache文件夾下:

    //緩存模式如下:
    //LOAD_CACHE_ONLY: 不使用網(wǎng)絡(luò)打毛,只讀取本地緩存數(shù)據(jù)
    //LOAD_DEFAULT: (默認(rèn))根據(jù)cache-control決定是否從網(wǎng)絡(luò)上取數(shù)據(jù)。
    //LOAD_NO_CACHE: 不使用緩存俩功,只從網(wǎng)絡(luò)獲取數(shù)據(jù).
    //LOAD_CACHE_ELSE_NETWORK,只要本地有碰声,無論是否過期诡蜓,或者no-cache,都使用緩存中的數(shù)據(jù)胰挑。

    //優(yōu)先使用緩存:
    webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
    //不使用緩存:
    webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE)

還有如下幾種:

 //開啟 DOM storage API 功能 較大存儲空間蔓罚,使用簡單
 settings.setDomStorageEnabled(true);
 //設(shè)置數(shù)據(jù)庫緩存路徑 存儲管理復(fù)雜數(shù)據(jù) 方便對數(shù)據(jù)進(jìn)行增加、刪除瞻颂、修改豺谈、查詢 不推薦使用
 settings.setDatabaseEnabled(true);
 final String dbPath = context.getApplicationContext().getDir("db", Context.MODE_PRIVATE).getPath();
 settings.setDatabasePath(dbPath);
 //開啟 Application Caches 功能 方便構(gòu)建離線APP 不推薦使用
 settings.setAppCacheEnabled(true);
 final String cachePath = context.getApplicationContext().getDir("cache", Context.MODE_PRIVATE).getPath();
 settings.setAppCachePath(cachePath);
 settings.setAppCacheMaxSize(5 * 1024 * 1024);
WebView緩存

4.2 WebViewClient:處理各種通知和請求事件。

WebViewClient類常用方法:

  • shouldOverrideUrlLoading():攔截URL請求贡这,重定向(有2個方法茬末,一個是兼容5.0以下,一個是兼容5.0以上盖矫,保險起見兩個都重寫)丽惭。
    無論返回true還是false,只要為WebView設(shè)置了WebViewClient辈双,系統(tǒng)就不會再將url交給第三方的瀏覽器去處理了责掏。q其中返回false,代表將url交給當(dāng)前WebView加載湃望,也就是正常的加載狀態(tài)换衬;shouldOverrideUrlLoading()返回true,代表開發(fā)者已經(jīng)對url進(jìn)行了處理证芭,WebView就不會再對這個url進(jìn)行加載了瞳浦。
    另外,使用post的方式加載頁面檩帐,此方法不會被調(diào)用术幔。

      webView.setWebViewClient(new WebViewClient(){
    
          //重定向URL請求,返回true表示攔截此url湃密,返回false表示不攔截此url诅挑。
          @Override
          public boolean shouldOverrideUrlLoading(WebView view, String url) {
              //作用1:重定向url
              if(url.startsWith("weixin://")){
                  url = url.replace("weixin://","http://");
                  webView.loadUrl(url);
              }
    
              //作用2:在本頁面的WebView打開四敞,防止外部瀏覽器打開此鏈接
              view.loadUrl(url);
              return true;
          }
      });
    
  • onPageStarted()onPageFinished():頁面加載時和頁面加載完畢時調(diào)用。

  • shouldOverrideKeyEvent():重寫此方法才能處理瀏覽器中的按鍵事件拔妥。

  • shouldInterceptRequest():頁面每一次請求資源之前都會調(diào)用這個方法(非UI線程調(diào)用)忿危。

  • onLoadResource():頁面加載資源時調(diào)用,每加載一個資源(比如圖片)就調(diào)用一次没龙。

  • onReceivedError():加載頁面的服務(wù)器出現(xiàn)錯誤(比如404)時回調(diào)铺厨。

  • onReceivedSslError():重寫此方法可以讓webview處理https請求。

  • doUpdateVisitedHistory():更新歷史記錄硬纤。

  • onFormResubmission():應(yīng)用程序重新請求網(wǎng)頁數(shù)據(jù)解滓。

  • onReceivedHttpAuthRequest():獲取返回信息授權(quán)請求。

  • onScaleChanged():WebView發(fā)生縮放改變時調(diào)用筝家。

  • onUnhandledKeyEvent():Key事件未被加載時調(diào)用洼裤。

補(bǔ)充,關(guān)鍵方法調(diào)用流程:
情況一:loadUrl()無重定向時

onPageStarted->onPageFinished

情況二:loadUrl()網(wǎng)頁A重定向到B時

onPageStarted->shouldOverrideUrlLoading->onPageStarted->onPageFinished->onPageFinished

情況三:在已加載的頁面中點(diǎn)擊鏈接溪王,加載頁面A(無重定向)

shouldOverrideUrlLoading->onPageStarted->onPageFinished

情況四:在已加載的頁面中點(diǎn)擊鏈接腮鞍,加載頁面A(頁面A重定向至頁面B)

shouldOverrideUrlLoading->onPageStarted->shouldOverrideUrlLoading->onPageStarted->onPageFinished->onPageFinished

情況五:執(zhí)行goBack/goForward/reload方法

onPageStarted->onPageFinished

情況六:發(fā)生資源加載

shouldInterceptRequest->onLoadResource

4.3 WebChromeClient:輔助 WebView 處理 Javascript 的對話框,網(wǎng)站圖標(biāo)莹菱,網(wǎng)站標(biāo)題等等移国。

WebChromeClient類常用方法:

  • onProgressChanged():獲得網(wǎng)頁的加載進(jìn)度并顯示。

  • onReceivedTitle():獲得網(wǎng)頁的標(biāo)題時回調(diào)道伟。

  • onReceivedIcon():獲得網(wǎng)頁的圖標(biāo)時回調(diào)迹缀。

  • onCreateWindow():打開新窗口時回調(diào)。

  • onCloseWindow():關(guān)閉窗口時回調(diào)蜜徽。

  • onJsAlert():網(wǎng)頁彈出提示框時觸發(fā)此方法裹芝。

      @Override
      public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
          Toast.makeText(MainActivity.this,"Im alert",Toast.LENGTH_SHORT).show();
    
          //部分機(jī)型只會彈出一次提示框,調(diào)用此方法即可解決此問題娜汁。
          result.cancel();
          //返回true表示不彈出系統(tǒng)的提示框嫂易,返回false表示彈出
          return true;
      }
    
  • onJsConfirm():網(wǎng)頁彈出確認(rèn)框時觸發(fā)此方法。

      @Override
      public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
    
          //confirm表示確認(rèn)掐禁,cancel表示取消怜械。
          result.confirm();
          //返回true表示不彈出系統(tǒng)的提示框,返回false表示彈出
          return true;
      }
    
  • onJsPrompt():網(wǎng)頁彈出輸入框時觸發(fā)此方法傅事。

      @Override
      public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
          //confirm表示確認(rèn)(并返回值)缕允,cancel表示取消。
          result.confirm("8848");
          //返回true表示不彈出系統(tǒng)的提示框蹭越,返回false表示彈出
          return true;
      }
    

4.4 Cookie 相關(guān)

WebView可以在需要的時候自動同步cookie障本,只需要獲得CookieManager的對象將cookie設(shè)置進(jìn)去就可以了。

從服務(wù)器的返回頭中取出 cookie 根據(jù)Http請求的客戶端不同,獲取 cookie 的方式也不同驾霜,請自行獲取案训。

4.4.1 設(shè)置cookie,若兩次設(shè)置相同粪糙,則覆蓋上一次的
/**
 * 將cookie設(shè)置到 WebView
 * @param url 要加載的 url
 * @param cookie 要同步的 cookie
 */
public static void syncCookie(String url,String cookie) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        CookieSyncManager.createInstance(context);
    }
    CookieManager cookieManager = CookieManager.getInstance();
    cookieManager.setCookie(url, cookie);//如果沒有特殊需求强霎,這里只需要將session id以"key=value"形式作為cookie即可
}

注意:

  1. 同步 cookie 要在 WebView 加載 url 之前,否則 WebView 無法獲得相應(yīng)的 cookie蓉冈,也就無法通過驗(yàn)證城舞。
    cookie應(yīng)該被及時更新,否則很可能導(dǎo)致WebView拿的是舊的session id和服務(wù)器進(jìn)行通信寞酿。
  2. cookie應(yīng)該被及時更新家夺,否則很可能導(dǎo)致WebView拿的是舊的session id和服務(wù)器進(jìn)行通信。
  3. CookieManager會將這個Cookie存入該應(yīng)用程序data/data/package_name/app_WebView/Cookies.db
4.4.2 獲取cookie
/**
 * 獲取指定 url 的cookie
 */
public static String syncCookie(String url) {
    CookieManager cookieManager = CookieManager.getInstance();
    return cookieManager.getCookie(url);
}
4.4.3 清除cookie
// 這個兩個在 API level 21 被拋棄
CookieManager.getInstance().removeSessionCookie();
CookieManager.getInstance().removeAllCookie();

// 推薦使用這兩個伐弹, level 21 新加的
CookieManager.getInstance().removeSessionCookies();// 移除所有過期 cookie
CookieManager.getInstance().removeAllCookies(); // 移除所有的 cookie

//設(shè)置清除cookie后的回調(diào)方法
private void removeCookie(Context context) {
    CookieManager.getInstance().removeAllCookies(new ValueCallback<Boolean>() {
        @Override
        public void onReceiveValue(Boolean value) {
            // 清除結(jié)果
        }
    });
}

5秦踪、其他使用場景介紹

5.1 長按保存圖片或者撥打電話

一般瀏覽器都有長按保存圖片或者撥打圖片的功能,實(shí)現(xiàn)這個功能和WebView.HitTestResult這個類有關(guān)掸茅,這個類會將你觸摸網(wǎng)頁的地方的類型和其他信息反饋給你。
WebView.HitTestResult的常用方法:

  • HitTestResult.getType():獲取所選中目標(biāo)的類型柠逞,可以是圖片昧狮,超鏈接,郵件板壮,電話等等逗鸣。
  • HitTestResult.getExtra():獲取額外的信息。

類型和意義:

WebView.HitTestResult.UNKNOWN_TYPE //未知類型

WebView.HitTestResult.PHONE_TYPE //電話類型

WebView.HitTestResult.EMAIL_TYPE //電子郵件類型

WebView.HitTestResult.GEO_TYPE //地圖類型

WebView.HitTestResult.SRC_ANCHOR_TYPE //超鏈接類型

WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE //帶有鏈接的圖片類型

WebView.HitTestResult.IMAGE_TYPE //單純的圖片類型

WebView.HitTestResult.EDIT_TEXT_TYPE //選中的文字類型

步驟:
1绰精、給WebView設(shè)置長按監(jiān)聽事件撒璧;
2、獲取WebView長按時的WebView.HitTestResult的事件類型笨使,如果是圖片卿樱,則做處理。

代碼:

webView.setOnLongClickListener(new View.OnLongClickListener() {
    @Override
    public boolean onLongClick(View view) {
        WebView.HitTestResult result = ((WebView) view).getHitTestResult();
        if(result != null){
            switch (result.getType()){
                case WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE:
                    String imgUrl = result.getExtra();
                    ...
                    return true;
                ...
            }
        }
        return false;
    }
});

Android與JavaScript交互

Android與JavaScript的交互依賴于WebView硫椰,具體交互方式可以查看我的另一篇文章:WebView與JavaScript的交互總結(jié)

WebView中常見問題

1繁调、Android5.0 WebView中Http和Https混合問題

1.1 在Android 5.0上 Webview 默認(rèn)不允許加載 Http 與 Https 混合內(nèi)容,解決辦法:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
     webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}

參數(shù)說明:

  • MIXED_CONTENT_ALWAYS_ALLOW:允許從任何來源加載內(nèi)容靶草,即使起源是不安全的蹄胰;
  • MIXED_CONTENT_NEVER_ALLOW:不允許Https加載Http的內(nèi)容,即不允許從安全的起源去加載一個不安全的資源奕翔;
  • MIXED_CONTENT_COMPATIBILITY_MODE:當(dāng)涉及到混合式內(nèi)容時裕寨,WebView 會嘗試去兼容最新Web瀏覽器的風(fēng)格。

1.2 設(shè)置WebView接受所有網(wǎng)站的證書

webView.setWebViewClient(new WebViewClient() {
            @Override
            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
                // handler.cancel();// Android默認(rèn)的處理方式
                handler.proceed();// 接受所有網(wǎng)站的證書
                // handleMessage(Message msg);// 進(jìn)行其他處理
            }
});

注意:一定要移除super.onReceivedSslError(view, handler, error)方法,否則不生效宾袜。

2捻艳、避免WebView引起的內(nèi)存泄漏

2.1 WebView所在的Activity新啟一個進(jìn)程

<activity
    android:name=".ui.activity.Html5Activity"
    android:process=":lyl.boon.process.web">
    <intent-filter>
        <action android:name="com.lyl.boon.ui.activity.htmlactivity"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>

結(jié)束的時候直接System.exit(0)退出當(dāng)前進(jìn)程。

2.2 動態(tài)創(chuàng)建WebView

在代碼中創(chuàng)建WebView试和,而不是在XML中創(chuàng)建:

LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        mWebView = new WebView(getApplicationContext());
        mWebView.setLayoutParams(params);
        mLayout.addView(mWebView);

2.3 Activity銷毀的時候先移除WebView

@Override
protected void onDestroy() {
    if (mWebView != null) {
        ((ViewGroup) mWebView.getParent()).removeView(mWebView);
        mWebView.destroy();
        mWebView = null;
    }
    super.onDestroy();
}

3讯泣、混淆導(dǎo)致JavaScript不可用

proguard-rules.pro文件中配置:

-keepattributes *Annotation*  
-keepattributes *JavascriptInterface*
-keep public class org.mq.study.webview.DemoJavaScriptInterface{
    public <methods>;
}

如果是內(nèi)部類:

-keepattributes *Annotation*  
-keepattributes *JavascriptInterface*
-keep public class org.mq.study.webview.webview.DemoJavaScriptInterface$InnerClass{
    public <methods>;
}

4、監(jiān)聽WebView網(wǎng)頁加載完畢

WebViewClientonPageFinished()方法不能保證一定是在網(wǎng)頁加載完畢后調(diào)用阅悍,也有可能是一些其他情況下也會調(diào)用好渠,因此,最好用WebChromeClientonProgressChanged()方法來監(jiān)聽:

webView.setWebChromeClient(new WebChromeClient() {

    @Override
    public void onProgressChanged(WebView view, int position) {
        progressBar.setProgress(position);
        if (position == 100) {
            progressBar.setVisibility(View.GONE);
        }
        super.onProgressChanged(view, position);
    }
});

5节视、 HTML5 video 在 WebView 全屏顯示

當(dāng)網(wǎng)頁全屏播放視頻時會調(diào)用WebChromeClient.onShowCustomView()方法拳锚,所以可以通過將 video 播放的視圖全屏達(dá)到目的。

@Override
public void onShowCustomView(View view, CustomViewCallback callback) {
    if (view instanceof FrameLayout && fullScreenView != null) {
        // A video wants to be shown
        this.videoViewContainer = (FrameLayout) view;
        this.videoViewCallback = callback;
        fullScreenView.addView(videoViewContainer, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        fullScreenView.setVisibility(View.VISIBLE);
        isVideoFullscreen = true;
    }
}

@Override
public void onHideCustomView() {
    if (isVideoFullscreen && fullScreenView != null) {
        // Hide the video view, remove it, and show the non-video view
        fullScreenView.setVisibility(View.INVISIBLE);
        fullScreenView.removeView(videoViewContainer);

        // Call back (only in API level <19, because in API level 19+ with chromium webview it crashes)
        if (videoViewCallback != null && !videoViewCallback.getClass().getName().contains(".chromium.")) {
            videoViewCallback.onCustomViewHidden();
        }

        isVideoFullscreen = false;
        videoViewContainer = null;
        videoViewCallback = null;
    }
}

注意:很多的手機(jī)版本在網(wǎng)頁視頻播放時是不會調(diào)用這個方法的寻行,所以這個方法局限性很大霍掺。

6、loadData()加載中文數(shù)據(jù)出現(xiàn)亂碼

使用loadDataWithBaseURL()方法代替loadData()加載數(shù)據(jù)拌蜘,不會出現(xiàn)亂碼問題杆烁。 為loadData()的mimeType參數(shù)傳入“text/html;charset=UTF-8”,也可以解決亂碼問題简卧。

7兔魂、外部JavaScript攻擊

由于應(yīng)用中的WebView沒有對file:///類型的url做限制,可能導(dǎo)致外部攻擊者利用Javascript代碼讀取本地隱私數(shù)據(jù)举娩。

7.1 如果WebView不需要使用file協(xié)議析校,直接禁用所有與file協(xié)議相關(guān)的功能即可(不影響對assets和resources資源的加載)。

webSettings.setAllowFileAccess(false);
webSettings.setAllowFileAccessFromFileURLs(false);
webSettings.setAllowUniversalAccessFromFileURLs(false);

7.2 如果WebView需要使用file協(xié)議铜涉,則應(yīng)該禁用file協(xié)議的Javascript功能智玻。具體方法為:在調(diào)用loadUrl方法前,以及在shouldOverrideUrlLoading方法中判斷url的scheme是否為file芙代。如果是file協(xié)議吊奢,就禁用Javascript,否則啟用Javascript纹烹。

//調(diào)用loadUrl前
if("file".equals(Uri.parse(url).getScheme())){//判斷是否為file協(xié)議
    webView.getSettings().setJavaScriptEnabled(false);
}else{
    webView.getSettings().setJavaScriptEnabled(true);
}
webView.loadUrl(url);

//WebViewClient中做的操作
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
    if("file".equals(request.getUrl().getScheme())){//判斷是否為file協(xié)議
        view.getSettings().setJavaScriptEnabled(false);
    }else{
        view.getSettings().setJavaScriptEnabled(true);
    }
    return false;
}

8事甜、WebView閃爍

WebView關(guān)閉硬件加速功能即可。

webView.setLayerType(View.LAYER_TYPE_SOFTWARE,null);

9滔韵、縮放引起的應(yīng)用崩潰

setDisplayZoomControls(true)方法會允許顯示系統(tǒng)縮放按鈕逻谦,這個縮放按鈕會在每次出現(xiàn)后的幾秒內(nèi)逐漸消失。但是在部分系統(tǒng)版本中陪蜻,如果在縮放按鈕消失前退出了Activity邦马,就可能引起應(yīng)用崩潰。

解決辦法:調(diào)用setDisplayZoomControls(false)方法不顯示系統(tǒng)縮放按鈕,反正使用手勢捏合動作就可以實(shí)現(xiàn)網(wǎng)頁的縮放功能了滋将。如果確實(shí)需要使用縮放按鈕邻悬,就需要在Activity的onDestroy()方法中隱藏WebView。

10随闽、webView控件padding不起作用

在一個布局文件中有一個WebView父丰,想使用padding屬性讓左右向內(nèi)留出一些空白,但是padding屬性不起左右掘宪,內(nèi)容照樣貼邊顯示蛾扇,反而移動了右邊滾動條的位置。android的bug魏滚,用一個外圍的layout包含webview镀首,可以有所改進(jìn),但不能完全解決鼠次。其實(shí)正確的做法是在webView的加載的css中增加padding,沒必要為了padding而更改xml布局文件更哄。

一個很不錯的WebViewActivity分享(來源于網(wǎng)絡(luò))

import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.KeyEvent;
import android.webkit.GeolocationPermissions;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import com.lyl.test.R;

public class Html5Activity extends AppCompatActivity {

    private String mUrl;

    private LinearLayout mLayout;
    private WebView mWebView;

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

        Bundle bundle = getIntent().getBundleExtra("bundle");
        mUrl = bundle.getString("url");

        Log.d("Url:", mUrl);

        mLayout = (LinearLayout) findViewById(R.id.web_layout);


        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        mWebView = new WebView(getApplicationContext());
        mWebView.setLayoutParams(params);
        mLayout.addView(mWebView);

        WebSettings mWebSettings = mWebView.getSettings();
        mWebSettings.setSupportZoom(true);
        mWebSettings.setLoadWithOverviewMode(true);
        mWebSettings.setUseWideViewPort(true);
        mWebSettings.setDefaultTextEncodingName("utf-8");
        mWebSettings.setLoadsImagesAutomatically(true);

        //調(diào)用JS方法.安卓版本大于17,加上注解 @JavascriptInterface
        mWebSettings.setJavaScriptEnabled(true);

        saveData(mWebSettings);

        newWin(mWebSettings);

        mWebView.setWebChromeClient(webChromeClient);
        mWebView.setWebViewClient(webViewClient);
        mWebView.loadUrl(mUrl);
    }

    @Override
    public void onPause() {
        super.onPause();
        webView.onPause();
        webView.pauseTimers(); //小心這個!P瓤堋成翩!暫停整個 WebView 所有布局、解析赦役、JS麻敌。
    }

    @Override
    public void onResume() {
        super.onResume();
        webView.onResume();
        webView.resumeTimers();
    }

    /**
     * 多窗口的問題
     */
    private void newWin(WebSettings mWebSettings) {
        //html中的_bank標(biāo)簽就是新建窗口打開,有時會打不開扩劝,需要加以下
        //然后 復(fù)寫 WebChromeClient的onCreateWindow方法
        mWebSettings.setSupportMultipleWindows(false);
        mWebSettings.setJavaScriptCanOpenWindowsAutomatically(true);
    }

    /**
     * HTML5數(shù)據(jù)存儲
     */
    private void saveData(WebSettings mWebSettings) {
        //有時候網(wǎng)頁需要自己保存一些關(guān)鍵數(shù)據(jù),Android WebView 需要自己設(shè)置
        mWebSettings.setDomStorageEnabled(true);
        mWebSettings.setDatabaseEnabled(true);
        mWebSettings.setAppCacheEnabled(true);
        String appCachePath = getApplicationContext().getCacheDir().getAbsolutePath();
        mWebSettings.setAppCachePath(appCachePath);
    }

    WebViewClient webViewClient = new WebViewClient(){

        /**
         * 多頁面在同一個WebView中打開,就是不新建activity或者調(diào)用系統(tǒng)瀏覽器打開
         */
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            view.loadUrl(url);
            return true;
        }

    };

    WebChromeClient webChromeClient = new WebChromeClient() {

        //=========HTML5定位==========================================================
        //需要先加入權(quán)限
        //<uses-permission android:name="android.permission.INTERNET"/>
        //<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
        //<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
        @Override
        public void onReceivedIcon(WebView view, Bitmap icon) {
            super.onReceivedIcon(view, icon);
        }

        @Override
        public void onGeolocationPermissionsHidePrompt() {
            super.onGeolocationPermissionsHidePrompt();
        }

        @Override
        public void onGeolocationPermissionsShowPrompt(final String origin, final GeolocationPermissions.Callback callback) {
            callback.invoke(origin, true, false);//注意個函數(shù)职辅,第二個參數(shù)就是是否同意定位權(quán)限棒呛,第三個是是否希望內(nèi)核記住
            super.onGeolocationPermissionsShowPrompt(origin, callback);
        }
        //=========HTML5定位==========================================================

        //=========多窗口的問題==========================================================
        @Override
        public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
            WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
            transport.setWebView(view);
            resultMsg.sendToTarget();
            return true;
        }
        //=========多窗口的問題==========================================================
    };

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && mWebView.canGoBack()) {
            mWebView.goBack();
            return true;
        }

        return super.onKeyDown(keyCode, event);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (mWebView != null) {
            mWebView.clearHistory();
            ((ViewGroup) mWebView.getParent()).removeView(mWebView);
            mWebView.loadUrl("about:blank");
            mWebView.stopLoading();
            mWebView.setWebChromeClient(null);
            mWebView.setWebViewClient(null);
            mWebView.destroy();
            mWebView = null;
        }
    }

}

參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末域携,一起剝皮案震驚了整個濱河市簇秒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌秀鞭,老刑警劉巖趋观,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異锋边,居然都是意外死亡皱坛,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進(jìn)店門豆巨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來剩辟,“玉大人,你說我怎么就攤上這事》妨裕” “怎么了熊户?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長吭服。 經(jīng)常有香客問我嚷堡,道長,這世上最難降的妖魔是什么艇棕? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任蝌戒,我火速辦了婚禮,結(jié)果婚禮上欠肾,老公的妹妹穿的比我還像新娘瓶颠。我一直安慰自己,他們只是感情好刺桃,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布粹淋。 她就那樣靜靜地躺著,像睡著了一般瑟慈。 火紅的嫁衣襯著肌膚如雪桃移。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天葛碧,我揣著相機(jī)與錄音借杰,去河邊找鬼。 笑死进泼,一個胖子當(dāng)著我的面吹牛蔗衡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播乳绕,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼绞惦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了洋措?” 一聲冷哼從身側(cè)響起济蝉,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎菠发,沒想到半個月后王滤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡滓鸠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年雁乡,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片糜俗。...
    茶點(diǎn)故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡蔗怠,死狀恐怖墩弯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情寞射,我是刑警寧澤渔工,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站桥温,受9級特大地震影響引矩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜侵浸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一旺韭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧掏觉,春花似錦区端、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至酱塔,卻和暖如春沥邻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背羊娃。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工唐全, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蕊玷。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓邮利,卻偏偏與公主長得像,于是被迫代替她去往敵國和親垃帅。 傳聞我的和親對象是個殘疾皇子延届,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)挺智,斷路器祷愉,智...
    卡卡羅2017閱讀 134,652評論 18 139
  • ¥開啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程窗宦,因...
    小菜c閱讀 6,402評論 0 17
  • 這篇博客主要來介紹 WebView 的相關(guān)使用方法赴涵,常見的幾個漏洞媒怯,開發(fā)中可能遇到的坑和最后解決相應(yīng)漏洞的源碼,以...
    Shawn_Dut閱讀 7,225評論 3 55
  • 前幾天髓窜,發(fā)生了一則工作小插曲扇苞,刷新了對付領(lǐng)導(dǎo)質(zhì)問后的新辦法欺殿。當(dāng)然該辦法只針對女人之間。并且生活中有小女人的一面的女...
    櫻桃小帥閱讀 859評論 0 0
  • 一直很喜歡手帳鳖敷,喜歡親手繪制的感覺 享受這個過程… 從未學(xué)過畫畫脖苏,但是很喜歡畫畫 有一個想開個人畫展的電視劇中存在...
    沉默凱蒂閱讀 310評論 0 0