1. 程式人生 > >全面的android webview開發使用詳解

全面的android webview開發使用詳解

1. 簡介

WebView是一個基於webkit引擎、展現web頁面的控制元件。

Android的Webview在低版本和高版本採用了不同的webkit版本核心,4.4後直接使用了Chrome。

2. 作用

  • 顯示和渲染Web頁面
  • 直接使用html檔案(網路上或本地assets中)作佈局
  • 可和JavaScript互動呼叫

WebView控制元件功能強大,除了具有一般View的屬性和設定外,還可以對url請求、頁面載入、渲染、頁面互動進行強大的處理。

3. 用法

1. 新增許可權:AndroidManifest.xml中設定許可權"android.permission.INTERNET",否則會出

Web page not available錯誤。

2. 在要Activity中生成一個WebView元件:WebView webView = new WebView(this);或者可以在activitylayout檔案裡新增webview控制元件

3. 設定WebView基本資訊:

mWebView = (WebView) findViewById(R.id.wb);

mWebView.getSettings().setJavaScriptEnabled(true);//支援javascript

mWebView.requestFocus();//觸控焦點起作用mWebView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);//

取消滾動條

mWebView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);//設定允許js彈出alert對話方塊

//load本地

mWebView.loadUrl("file:///android_asset/hellotest.html");

//load線上

//mWebView.loadUrl("http://www.google.com");

//js訪問android,定義介面

mWebView.addJavascriptInterface(new JsInteration(), "control");

//設定了Alert才會彈出,重新onJsAlert

()方法return true可以自定義處理資訊

mWebView.setWebChromeClient(new WebChromeClient() {   

@Override   

public boolean onJsAlert(WebView view, String url, String message, JsResult result) {       

//return super.onJsAlert(view, url, message,result);       

Toast.makeText(MainActivity.this, message, Toast.LENGTH_LONG).show();       

return true; 

  }});

4. 設定WebView要顯示的網頁:網際網路用本地檔案用:webView.loadUrl("file:///android_asset/XX.html");本地檔案存放在:assets檔案中

5. 如果希望點選連結由自己處理,而不是新開Android的系統browser中響應該連結。給WebView新增一個事件監聽物件(WebViewClient)並重寫其中的一些方法: shouldOverrideUrlLoading:對網頁中超連結按鈕的響應。當按下某個連線時WebViewClient會呼叫這個方法,並傳遞引數

mWebView.setWebViewClient(new WebViewClient(){

      @Override

      public booleanshouldOverrideUrlLoading(WebView view, String url) {

          view.loadUrl(url);

          return true;

      }

  });

6. 處理https請求
webView
預設是不處理https請求的,頁面顯示空白,需要進行如下設定:

webView.setWebViewClient(new WebViewClient() {

@Override public void onReceivedSslError(WebView view,

SslErrorHandler handler, SslError error) {

handler.proceed();

// handler.cancel();

// handler.handleMessage(null); } });

onReceivedSslErrorwebView處理ssl證書設定

其中handler.proceed();表示等待證書響應
handler.cancel();
表示掛起連線,為預設方式
handler.handleMessage(null);
可做其他處理另外還有其他一些可重寫的方法
1
,接收到Http請求的事件onReceivedHttpAuthRequest(WebView view,HttpAuthHandler handler, String host, String realm)
2
,載入頁面完成的事件public void onPageFinished(WebView view,String url){ }
同樣道理,我們知道一個頁面載入完成,於是我們可以關閉loading條,切換程式動作。
3
,載入頁面開始的事件public void onPageStarted(WebView view,String url, Bitmap favicon) { }
這個事件就是開始載入頁面呼叫的,通常我們可以在這設定一個loading的頁面,告訴使用者程式在等待網路響應。通過這幾個事件,我們可以很輕鬆的控制程式操作,一邊用著瀏覽器顯示內容,一邊監控著使用者操作實現我們需要的各種顯示方式,同時可以防止使用者產生誤操作。

7. 如果用webview點連結看了很多頁以後,如果不做任何處理,點選系統“Back”鍵,整個瀏覽器會呼叫finish()而結束自身,如果希望瀏覽的網頁回退而不是退出瀏覽器,需要在當前Activity中處理並消費掉該Back事件。覆蓋Activity類的onKeyDown(int keyCoder,KeyEvent event)方法。

 @Overridepublicboolean onKeyDown(int keyCode, KeyEvent event) { 

if (keyCode == KeyEvent.KEYCODE_BACK &&webView.canGoBack()) {   

   webView.goBack();// 返回前一個頁面

return true;  

 }   

returnsuper.onKeyDown(keyCode, event);

}

8. jsjava程式碼互呼叫

java調js:呼叫webView.load("javascript:someFunction()");

這樣可以呼叫webView裡頁面上的全域性方法。這不是什麼新鮮東西,你在網頁中也可以這麼做,試試在瀏覽器位址列輸入javascript:alert("427studio");也可以在瀏覽器位址列裡呼叫全域性方法。

    //呼叫js方法,第一個引數是js方法名,後面的引數是js方法的引數列表
    void doJs(String function, Object... params){
        StringBuilder result = newStringBuilder();
       result.append("javascript:").append(function).append("(");
        for(int i = 0; i <params.length; i++){
           result.append("'").append(params[i].toString()).append("'");
            if(i <params.length - 1){
               result.append(",");
            }
        }
        result.append(")");
        String jsStr =result.toString();
        webView.loadUrl(jsStr);
    }
}

js調java:呼叫webView.addJavascriptInterface(somePOJO,"varName");

讓一個java物件成為webview裡面網頁的window物件的varName屬性,就好像執行了window.varName = somePOJO一樣,因為window是全域性上下文,js即可以用訪問全域性變數的方式訪問這個java物件了,然後呼叫這個物件的函式即可,如果somePOJO這個物件有個public void doIt()方法,則可以這樣呼叫它:someButton.onclick=function(){varName.doIt();}

 //要用來被js呼叫的java方法
    @JavascriptInterface
    public void javaDoIt(final String str){
        Message msg = newMessage();
        Bundle bundle = newBundle();
       bundle.putString("str", str);
        msg.setData(bundle);
       handler.sendMessage(msg);
    }

4.WebViewClient API詳解

1)網頁載入時機部分

publicboolean shouldOverrideUrlLoading(WebViewview, String url) {

          view.loadUrl(url);

returntrue;

}

當載入的網頁需要重定向的時候就會回撥這個函式告知我們應用程式是否需要接管控制網頁載入,如果應用程式接管,並且return true意味著主程式接管網頁載入,如果返回falsewebview自己處理。

引數說明:

@param view 接收WebViewClient的那個例項,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是這個webview

@param url    即將要被載入的url

@return  true 當前應用程式要自己處理這個url返回false則不處理。

Tips

(1) 當請求的方式是"POST"方式時這個回撥是不會通知的。

(2) 當我們訪問的地址需要我們應用程式自己處理的時候,可以在這裡截獲,比如我們發現跳轉到的是一個market的連結,那麼我們可以直接跳轉到應用市場,或者其他app

1. public void onPageStarted(WebView view, String url, Bitmap favicon)  

當核心開始載入訪問的url時,會通知應用程式,對每個main frame這個函式只會被呼叫一次,頁面包含iframe 或者framesets不會另外呼叫一次onPageStarted,當網頁內內嵌的frame 發生改變時也不會呼叫onPageStarted

引數說明:

@param view 接收WebViewClient的那個例項,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是這個webview

@param url    即將要被載入的url

@param favicon 如果這個favicon已經儲存在本地資料庫中,則會返回這個網頁的favicon,否則返回為null

Tips

(1) iframe 可能不少人不知道什麼含義,這裡我解釋下,iframe 我們載入的一張,下面有很多連結,我們隨便點選一個連結是即當前host的一個iframe.

(2) 有個問題可能是開發者困惑的,onPageStartedshouldOverrideUrlLoading 在網頁載入過程中這兩個函式到底哪個先被呼叫。

當我們通過loadUrl的方式重新載入一個網址時候,這時候會先呼叫onPageStarted再呼叫shouldOverrideUrlLoading,當我們在開啟的這個網址點選一個link,這時候會先呼叫shouldOverrideUrlLoading 再呼叫onPageStarted。不過shouldOverrideUrlLoading不一定每次都被呼叫,只有需要的時候才會被呼叫。

1. public void onPageFinished(WebView view, String url)  

當核心載入完當前頁面時會通知我們的應用程式,這個函式只有在main frame情況下才會被呼叫,當呼叫這個函式之後,渲染的圖片不會被更新,如果需要獲得新圖片的通知可以使用@link WebView.PictureListener#onNewPicture

引數說明:

@param view 接收WebViewClient的那個例項,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是這個webview

@param url    即將要被載入的url

public void onLoadResource(WebView view, String url)  

通知應用程式WebView即將載入url制定的資源

引數說明:

@param view 接收WebViewClient的那個例項,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是這個webview

@param url    即將載入的url資源

1. public WebResourceResponse shouldInterceptRequest(WebView view,  

2.             String url)  

通知應用程式核心即將載入url制定的資源,應用程式可以返回本地的資源提供給核心,若本地處理返回資料,核心不從網路上獲取資料。

引數說明:

@param view 接收WebViewClient的那個例項,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是這個webview

@param url   raw url 制定的資源

@return 返回WebResourceResponse包含資料物件,或者返回null

Tips

這個回撥並不一定在UI執行緒執行,所以我們需要注意在這裡操作View或者私有資料相關的動作。

如果我們需要改變網頁的背景,或者需要實現網頁頁面顏色定製化的需求,可以在這個回撥時機處理。

1. public void onReceivedError(WebView view, int errorCode,  

2.             String description, String failingUrl)  

當瀏覽器訪問制定的網址發生錯誤時會通知我們應用程式,比如網路錯誤。

引數說明:

@param view 接收WebViewClient的那個例項,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是這個webview

@param errorCode 錯誤號可以在WebViewClient.ERROR_*裡面找到對應的錯誤名稱。

@param description 描述錯誤的資訊

@param failingUrl  當前訪問失敗的url,注意並不一定是我們主url

Tips

onReceiveError我們可以自定義網頁的錯誤頁面。

1. public void onFormResubmission(WebView view, Message dontResend,  

2.             Message resend)  

如果瀏覽器需要重新發送POST請求,可以通過這個時機來處理。預設是不重新發送資料。

引數說明:

@param view 接收WebViewClient的那個例項,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是這個webview

@param dontResent 當瀏覽器不需要重新發送資料時,可以使用這個引數。

@param resent 當瀏覽器需要重新發送資料時,可以使用這個引數。

1. public void doUpdateVisitedHistory(WebView view, String url,  

2.             boolean isReload)  

通知應用程式可以將當前的url儲存在資料庫中,意味著當前的訪問url已經生效並被記錄在核心當中。這個函式在網頁載入過程中只會被呼叫一次。注意網頁前進後退並不會回撥這個函式。

引數說明:

@param view 接收WebViewClient的那個例項,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是這個webview

@param url 當前正在訪問的url 

@ param isReload 如果是true這個是正在被reloadurl

1. public void onReceivedSslError(WebView view, SslErrorHandler handler,  

2.             SslError error)  

當網頁載入資源過程中發現SSL錯誤會呼叫此方法。我們應用程式必須做出響應,是取消請求handler.cancel(),還是繼續請求handler.proceed();核心的預設行為是handler.cancel();

引數說明:

@param view 接收WebViewClient的那個例項,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是這個webview

@param handler 處理使用者請求的物件。

@param error  SSL錯誤物件

Tips

核心會記住本次選擇,如果下次還有相同的錯誤,核心會直接執行之前選擇的結果。

1. public void onReceivedHttpAuthRequest(WebView view,  

2.             HttpAuthHandler handler, String host, String realm)  

通知應用程式WebView接收到了一個Http auth的請求,應用程式可以使用supplied 設定webview的響應請求。預設行為是cancel 本次請求。

引數說明:

@param view 接收WebViewClient的那個例項,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是這個webview

@param handler 用來響應WebView請求的物件

@param host  請求認證的host

@param realm 認真請求所在的域

1. public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event)   

提供應用程式同步一個處理按鍵事件的機會,選單快捷鍵需要被過濾掉。如果返回truewebview不處理該事件,如果返回false webview會一直處理這個事件,因此在view 鏈上沒有一個父類可以響應到這個事件。預設行為是return false

引數說明:

@param view 接收WebViewClient的那個例項,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是這個webview

@param  event 鍵盤事件名

@return  如果返回true,應用程式處理該時間,返回false 交有webview處理。

1. public void onScaleChanged(WebView view, float oldScale, float newScale)  

通知應用程式webview要被scale。應用程式可以處理改事件,比如調整適配螢幕。

1. public void onReceivedLoginRequest(WebView view, String realm,  

2.             String account, String args)  

通知應用程式有個自動登入的帳號過程

引數說明:

@param view 請求登陸的webview

@param realm 賬戶的域名,用來查詢賬戶。

@param account 一個可選的賬戶,如果是null 需要和本地的賬戶進行check如果是一個可用的賬戶,則提供登入。

@param  args  驗證制定引數的登入使用者

3.WebChromeClient 基本使用

5webview遇到的那些坑與解決方法

1. WebView的記憶體洩露。這個問題,很難清晰描述,你在谷歌裡搜 webview leadmemory 能搜到很多結果甚至還有給谷歌提交的issue 哈哈,我也無法給出一個清晰的答案在什麼時候什麼版本那些手機上一定會出現記憶體洩露,但是根據一些monkey結果來看,有時,webview記憶體洩露的情況還是很嚴重的,尤其是當你載入的頁面比較龐大的時候。解決方案參考下微信和qq的做法,試了一下是目前效果最好的,就是當你要用webview的時候,記得最好另外單獨開一個程序去使用webview 並且當這個程序結束時,請手動呼叫System.exit(0)這是目前對於webview 記憶體洩露最好的解決方案。使用此方法所有因為webview引發的資源無法釋放等問題全部可以解決。

如何避免WebView記憶體洩露

3.4.1不在xml中定義 Webview ,而是在需要的時候在Activity中建立,並且Context使用 getApplicationgContext()

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

3.4.2 在 Activity 銷燬( WebView )的時候,先讓 WebView 載入null內容,然後移除 WebView,再銷燬 WebView,最後置空。

@Override
protectedvoidonDestroy() {
if (mWebView != null) {
            mWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
            mWebView.clearHistory();
            ((ViewGroup) mWebView.getParent()).removeView(mWebView);
            mWebView.destroy();
            mWebView = null;
        }
super.onDestroy();
    }

2. getSettings().setBuiltInZoomControls(true)引發的crush這個方法呼叫以後如果你觸控式螢幕幕彈出那個提示框還沒消失的時候你如果activity結束了就會報錯了。3.0以上 4.4以下很多手機會出現這種情況所以為了規避他,我們通常是在activityonDestroy方法裡手動的將webiew設定成setVisibility(View.GONE)

3.onPageFinished 函式到底有用沒有?多數開發者都是參考的這個上面的高票答案。但其實根據我自己觀察,這個函式並沒有什麼卵用,有的時候是提前結束,有的時候就遲遲無法結束,你信這個函式還不如信上帝,甚至於onProgressChanged這個函式都比onPageFinished 要準一些。如果你的產品經理堅持你一定要實現這種功能的話,我建議你提早結束他,否則卡在那使用者遲遲動不了這種體驗不好。有空的同學可以跟一下原始碼,onPageFinished 在不同的核心裡呼叫的時機都不一樣。說實話我也很醉。。。這個問題有完美解決方案的請知會我一下。。。

4.後臺無法釋放js 導致耗電。這個可能很少有人知道,你如果webview載入的html有一些js 一直在執行比如動畫之類的東西,如果此刻webview 掛在了後臺這些資源是不會被釋放,使用者也無法感知,導致一直佔有cpu 耗電特別快,所以大家記住了,如果遇到這種情況請在onstoponresume裡分別把setJavaScriptEnabled();給設定成falsetrue

5.如果實在不想用開額外程序的方式解決webview 記憶體洩露的問題,那麼下面的方法很大程度上可以避免這種情況

public void releaseAllWebViewCallback() { 
if (android.os.Build.VERSION.SDK_INT < 16) { 
try { 
 Field field = WebView.class.getDeclaredField("mWebViewCore"); 
field = field.getType().getDeclaredField("mBrowserFrame"); 
 field = field.getType().getDeclaredField("sConfigCallback");
field.setAccessible(true); 
 field.set(null, null);
 } catch (NoSuchFieldException e) {
if (BuildConfig.DEBUG) {
  e.printStackTrace();
  }
 } catch (IllegalAccessException e) {
if (BuildConfig.DEBUG) {
  e.printStackTrace();
  }
  }
 } else {
try {
 Field sConfigCallback = Class.forName("android.webkit.BrowserFrame").getDeclaredField("sConfigCallback");
if (sConfigCallback != null) {
 sConfigCallback.setAccessible(true);
 sConfigCallback.set(null, null);
  }
 } catch (NoSuchFieldException e) {
if (BuildConfig.DEBUG) {
  e.printStackTrace();
  }
 } catch (ClassNotFoundException e) {
if (BuildConfig.DEBUG) {
  e.printStackTrace();
  }
 } catch (IllegalAccessException e) {
if (BuildConfig.DEBUG) {
  e.printStackTrace();
  }
  }
  }
 }

webview destroy方法裡呼叫這個方法就行了。