Android Webview的一些使用總結和遇到過得坑

_小馬快跑_發表於2017-12-15

WebView 用來顯示網頁的一個View,它使用WebKit渲染引擎顯示web頁面,可以載入線上的或者本地的html頁面,WebView可以對頁面進行一系列操作,如歷史頁面的向前、向後,放大和縮小,執行文字搜尋,與JS互動等等;

在使用Webview時,請記得在AndroidManifest.xml檔案中宣告INTERNET許可權:

<uses-permission android:name="android.permission.INTERNET" />
複製程式碼

預設情況下,WebView不支援JavaScript,web頁面的錯誤也會被忽略,如果只是用Webview來顯示網頁而不用互動,預設配置就可以了,如果需要互動,就需要自定義配置了。

  • WebView初始化及載入URL

1、通過XML初始化:

<WebView 
    android:id="@+id/webview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
/>
複製程式碼

或者直接New:

WebView webview = new WebView(this);
複製程式碼

2、載入URL:

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.loadUrl("http://www.example.com");
或者可以直接載入Html:
String summary = "<html><body>You scored <b>192</b> points.</body></html>";
myWebView.loadData(summary, "text/html", null);
複製程式碼

如果在載入URL的時候想新增Header資訊,可以複寫loadUrl()方法,如:

@Override
 public void loadUrl(String url) {
     Map<String, String> map = new HashMap<String, String>();
     map.put("ajax", "true");
     map.put("appversion", SharedPreferencesUtil.getAppVersion(getContext()));
     map.put("clientid", SharedPreferencesUtil.getClientId(getContext()));
     loadUrl(url, map);
 }
複製程式碼
  • WebSettings

WebSettings 中可以對WebView進行一系列配置,如:

 WebSettings settings = getSettings();
 //預設是false 設定true允許和js互動
 settings.setJavaScriptEnabled(true);
 //  WebSettings.LOAD_DEFAULT 如果本地快取可用且沒有過期則使用本地快取,否載入網路資料 預設值
 //  WebSettings.LOAD_CACHE_ELSE_NETWORK 優先載入本地快取資料,無論快取是否過期
 //  WebSettings.LOAD_NO_CACHE  只載入網路資料,不載入本地快取
 //  WebSettings.LOAD_CACHE_ONLY 只載入快取資料,不載入網路資料
 //Tips:有網路可以使用LOAD_DEFAULT 沒有網時用LOAD_CACHE_ELSE_NETWORK
 settings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
 //開啟 DOM storage API 功能 較大儲存空間,使用簡單
 settings.setDomStorageEnabled(true);
 //設定資料庫快取路徑 儲存管理複雜資料 方便對資料進行增加、刪除、修改、查詢 不推薦使用
 settings.setDatabaseEnabled(true);
 final String dbPath = context.getApplicationContext().getDir("db", Context.MODE_PRIVATE).getPath();
 settings.setDatabasePath(dbPath);
 //開啟 Application Caches 功能 方便構建離線APP 不推薦使用
 settings.setAppCacheEnabled(true);
 final String cachePath = context.getApplicationContext().getDir("cache", Context.MODE_PRIVATE).getPath();
 settings.setAppCachePath(cachePath);
 settings.setAppCacheMaxSize(5 * 1024 * 1024);
複製程式碼

Webview中可以設定的幾種快取對比:

bugly.png
圖片來自騰訊Bugly,詳情請參閱:H5 快取機制淺析 - 移動端 Web 載入效能優化

  • WebviewClient

WebCromeClient常用方法: 1、shouldOverrideUrlLoading(WebView view, String url) 在API 24以後過時,當一個url即將被webview載入時,給Application一個機會來接管處理這個url,方法返回true代表Application自己處理url;返回false代表Webview處理url。 舉個例子,專案中需要處理傳過來的URL是一個事件還是一個HTTP連結,可以通過自定義協議頭 (nativeapi://) 來過濾,如:

@Override
 public boolean shouldOverrideUrlLoading(WebView view, String url) {
     Uri uri = Uri.parse(url);
     String scheme = uri.getScheme();
     if (TextUtils.isEmpty(scheme)) return true;
     if (scheme.equals("nativeapi")) {
         //如定義nativeapi://showImg是用來檢視大圖,這裡新增檢視大圖邏輯
         return true;
     } else if (scheme.equals("http") || scheme.equals("https")) {
         //處理http協議
         if (Uri.parse(url).getHost().equals("www.example.com")) {
            // 內部網址,不攔截,用自己的webview載入
             return false;
         } else {
             //跳轉外部瀏覽器
             Intent intent = new Intent(Intent.ACTION_VIEW, uri);
             context.startActivity(intent);
             return true;
         }
     }
     return super.shouldOverrideUrlLoading(view, url);
 }
複製程式碼

注:如果使用的是Post請求方式,則此方法不會被回撥

2、shouldOverrideUrlLoading(WebView view, WebResourceRequest request) 在API 24以後新加的,使用同上。

3、shouldInterceptRequest(WebView view, String url) 在API 21以後過時,通知Application載入資源的請求並返回請求的資源,如果返回值是Null,Webview仍然會按正常載入資源;否則返回的資料將會被使用。 注:回撥發生在子執行緒中,不能直接進行UI操作

4、shouldInterceptRequest(WebView view, WebResourceRequest request) 在API 21以後新加,使用同上。

5、onPageStarted(WebView view, String url, Bitmap favicon) 通知Application頁面已經開始載入資源,頁面載入過程中,onPageStarted至多會被執行一次。

6、onPageFinished(WebView view, String url) 通知Application頁面已經載入完畢。

7、onReceivedError(WebView view, int errorCode, String description, String failingUrl) 通知Application有錯誤發生,這些錯誤是不可恢復的(即主要的資源不可用)。errorCode引數對應於一個ERROR_ *常量

  • WebCromeClient

1、onProgressChanged(WebView view, int newProgress) 通知Application的載入進度,newProgress取值範圍[0,100],可以通過這個方法來編寫一個帶載入進度條的Webview,具體例子請參考:Android 編寫一個帶進度條的Webview

2、onReceivedTitle(WebView view, String title) 當載入頁面標題有改變時會通知Application,title即為新標題。

  • 控制Webview載入歷史網頁

WebView重寫URL載入時,它會自動累積的歷史訪問的web頁面。可以通過向後goBack()和向前goForward()。 舉例,可以在Activity中的回退鍵控制向後回退到前一個頁面:

@Override
 public boolean onKeyDown(int keyCode, KeyEvent event) {
     // Check if the key event was the Back button and if there's history
     if ((keyCode == KeyEvent.KEYCODE_BACK) && webview.canGoBack()) {
         webview.goBack();
         return true;
     }
     // If it wasn't the Back key or there's no web page history, bubble up to the default
     // system behavior (probably exit the activity)
     return super.onKeyDown(keyCode, event);
    }
複製程式碼
  • Webview和Js互動

1、Js呼叫Android介面:

public class WebAppInterface {
    Context mContext;

    /** Instantiate the interface and set the context */
    WebAppInterface(Context c) {
        mContext = c;
    }

    /** Show a toast from the web page */
    @JavascriptInterface
    public void showToast(String toast) {
        Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
    }
}
複製程式碼

SDK>=17(Android4.2)以上,必須新增**@JavascriptInterface**宣告,然後通過 addJavascriptInterface() 方式供Js呼叫,如:

webView.addJavascriptInterface(new WebAppInterface(this), "android");
複製程式碼

通過上面的配置,就可以在Js中呼叫了:

<input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" />
<script type="text/javascript">
    function showAndroidToast(toast) {
        //呼叫Android中的showToast方法
        Android.showToast(toast);
    }
</script>
複製程式碼

2、Android呼叫Js的介面: 可以通過webview.loadUrl("javascript:JsMethod()")方式載入Js介面,如果有引數,直接加到JsMethod()裡面即可,下面封裝了兩個方法,分別是載入帶引數和不帶引數的Js函式:

 /**
     * 載入帶引數的JS函式
     *
     * @param JsName JS函式名
     * @param params 不定引數
     */
    public void loadJSWithParam(String JsName, String... params) {
        String TotalParam = "";
        for (int i = 0; i < params.length; i++) {
            if (i == params.length - 1) {
                //最後一個
                TotalParam += (params[i]);
            } else {
                TotalParam += (params[i] + "','");
            }
        }
        this.loadUrl("javascript:" + JsName + "('" + TotalParam + "')");
    }

    /**
     * 載入不帶引數的JS函式
     *
     * @param JsName JS函式名
     */
    public void loadJS(String JsName) {
        this.loadUrl("javascript:" + JsName + "()");
    }
複製程式碼
  • Webview的一些優化和遇到的坑

1、Webview開啟一個連結,播放一段音樂,退出Activity時音樂還在後臺播放,可以通過在Activity的onPause中呼叫webview.onPause()解決,並在Activity的onResume中呼叫webview.onResume()恢復,如下:

  @Override
    protected void onPause() {
       h5_webview.onPause();
       h5_webview.pauseTimers();
       super.onPause();
    }
 @Override
    protected void onResume() {
       h5_webview.onResume();
       h5_webview.resumeTimers();
       super.onResume();
    }
複製程式碼

Webview的**onPause()**方法官網是這麼解釋的:

Does a best-effort attempt to pause any processing that can be paused safely, such as animations and geolocation. Note that this call does not pause JavaScript. To pause JavaScript globally, use pauseTimers(). To resume WebView, call onResume().

通知核心嘗試停止所有處理,如動畫和地理位置,但是不能停止Js,如果想全域性停止Js,可以呼叫pauseTimers()全域性停止Js,呼叫onResume()恢復。

2、5.0 以後的WebView載入的連結為Https開頭,但是連結裡面的內容,比如圖片為Http連結,這時候,圖片就會載入不出來,解決方法:

 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 
 webSetting.setMixedContentMode(webSetting.getMixedContentMode());
  }
複製程式碼

原因是5.0之後不支援Https和Http的混合模式,具體可參看:Android5.0 WebView中Http和Https混合問題

3、WebView與JavaScript相互呼叫時,如果是debug沒有配置混淆時,呼叫時沒問題的,但是當設定混淆後發現無法正常呼叫了,解決方法: 在proguard-rules.pro檔案中配置:

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

如果是內部類:

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

4、WebView中存在的漏洞,參看:你不知道的 Android WebView 使用漏洞

相關文章