Android WebView簡單使用以及實現native與h5交互

一、WebView

谷歌提供的系統組件,用來加載和展現html網頁,其采用webkit內核驅動,來實現網頁瀏覽功能。

擁有load() URL和本地html文件。

 // 云端 webView.loadUrl("https://www.baidu.com");
 // 本地 webView.loadUrl("file:///android_asset/demo.html");

注意:

  1. loadUrl()必須在主線程中執行。
  2. 加載在線網頁地址是會用到聯網permission權限的,所以需要在AndroidManifest.xml中寫入下面代碼申請權限:
    <uses-permission android:name="android.permission.INTERNET" />
  3. 打開本地html文件時,是不需要設置WebViewClient,對應的asstes目錄的url為:file:///android_asset/xxxxx
  4. WebView基本設置
    如果我們需要設置WebView的屬性,是通過WebView.getSettings()獲取設置WebView的WebSettings對象,然后調用WebSettings中的方法來實現的。
// 是否支持縮放,配合方法setBuiltInZoomControls使用,默認true  
setSupportZoom(boolean support) 
//是否需要用戶手勢來播放Media,默認true 
setMediaPlaybackRequiresUserGesture(boolean require)  
 是否顯示窗口懸浮的縮放控制,默認true 
setDisplayZoomControls(boolean enabled)  
是否允許訪問WebView內部文件,默認true 
setAllowFileAccess(boolean allow)  
是否保存表單數據,默認false 
setSaveFormData(boolean save)  
// 設置頁面文字縮放百分比,默認100% 
setTextZoom(int textZoom)  
。。。。。。

二、WebViewClient

WebViewClient主要輔助WebView執行處理各種響應請求事件的,比如:

  1. onLoadResource(WebView view, String url)
    在加載頁面資源時會調用,每一個資源(比如圖片)的加載都會調用一次。
public void onLoadResource(WebView view, String url) {           
    // TODO Auto-generated method stub            
   if (DEBUG) {              
      Log.d(TAG, " onLoadResource ");           
    }            
   super.onLoadResource(view, url);       
}
  • onPageStarted(WebView view, String url, Bitmap favicon)
    在頁面加載開始時調用。
public void onPageStarted(WebView view, String url, Bitmap favicon) { 
          // TODO Auto-generated method stub     
        if (DEBUG) { 
              Log.d(TAG, " onPageStarted ");  
          }
          if (url.endsWith(".apk")) {   
                download(url);//下載處理   
          }  
         super.onPageStarted(view, url, favicon); 
}
  • onPageFinished(WebView view, String url)
    在頁面加載結束時調用。
  • onReceivedError(WebView view, int errorCode,String description, String failingUrl)
    加載錯誤的時候會回調,在其中可做錯誤處理,比如再請求加載一次,或者提示404的錯誤頁面
    這里有四個參數:
  • WebView view:當前的WebView實例
  1. int errorCode:錯誤碼
  2. String description:錯誤描述
  3. String failingUrl:當前出錯的URL
    如加載返回錯誤時,重新加載錯誤頁面:
mWebView.setWebViewClient(new WebViewClient(){  
    @Override  
    public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {  
        super.onReceivedError(view, errorCode, description, failingUrl);  
        mWebView.loadUrl("file:///android_asset/error.html");  
    }  
});  
  • shouldOverrideUrlLoading(WebView view, String url)
    攔截 url 跳轉,在里邊添加點擊鏈接跳轉或者操作
public boolean shouldOverrideUrlLoading(WebView view, String url) {        
  //攔截某個URL,將其轉換成其它URL可以在這里做
   view.loadUrl(url);      
   return true; 
}

在點擊請求的是鏈接是才會調用,重寫此方法返回true表明點擊網頁里面的鏈接還是在當前的webview里跳轉,不跳到瀏覽器那邊。
比如,我們攔截所有包含“blog.csdn.net”的地址,將其替換成”www.baidu.com”:

public boolean shouldOverrideUrlLoading(WebView view, String url) {  
   if (url.contains("blog.csdn.net")){  
        view.loadUrl("http://www.baidu.com");  
    }else {  
        view.loadUrl(url);  
    }  
   return true;  
}  

或者

mWebView.setWebViewClient(new WebViewClient(){  
    @Override  
    public boolean shouldOverrideUrlLoading(WebView view, String url) {  
        if (url.contains("blog.csdn.net")){  
            view.loadUrl("http://www.baidu.com");  
        }  
        return false;  
    }  
}  

在利用shouldOverrideUrlLoading來攔截URL時,如果return true,則會屏蔽系統默認的顯示URL結果的行為,不需要處理的URL也需要調用loadUrl()來加載進WebVIew,不然就會出現白屏;如果return false,則系統默認的加載URL行為是不會被屏蔽的,所以一般建議大家return false,我們只關心我們關心的攔截內容,對于不攔截的內容,讓系統自己來處理即可。

  • shouldInterceptRequest(WebView view, String url)
    在每一次請求資源時,都會通過這個函數來回調,比如超鏈接、JS文件、CSS文件、圖片等,也就是說瀏覽器中每一次請求資源時,都會回調回來,無論任何資源!但是必須注意的是shouldInterceptRequest函數是在非UI線程中執行的,在其中不能直接做UI操作,如果需要做UI操作,則需要利用Handler來實現,該函數聲明如下:
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {  
    return null;  
}  

該函數會在請求資源前調用,我們可以通過返回WebResourceResponse的處理結果來讓WebView直接使用我們的處理結果。如果我們不想處理,則直接返回null,系統會繼續加載該資源。 利用這個特性,我們可以解決一個需求:假如網頁中需要加載本地的圖片,我們就可以通過攔截shouldInterceptRequest,并返回結果即可 。
如下:

mWebView.setWebViewClient(new WebViewClient(){  
            @Override  
            public WebResourceResponse shouldInterceptRequest(WebView view, String url) {  
                try {  
                    if (url.equals("http://localhost/pic.png")) {  
                        AssetFileDescriptor fileDescriptor =  getAssets().openFd("drawable.jpg");  
                        InputStream stream = fileDescriptor.createInputStream();  
                        WebResourceResponse response = new WebResourceResponse("image/png", "UTF-8", stream);  
                        return response;  
                    }  
                }catch (Exception e){  
                    Log.e(TAG,e.getMessage());  
                }  
                return super.shouldInterceptRequest(view, url);  
            }  
        }); 

當發現當前加載資源的url是我們自定義的http://localhost/pic.png時,就直接將本地的圖片drawable.jpg作為結果返回。

  • onReceivedSslError(WebView view, SslErrorHandler handler,SslError error)
    HTTPS協議是通過SSL來通信的,所以當使用HTTPS通信的網址(以https://開頭的網站)出現錯誤時,就會通過onReceivedSslError回調通知過來,它的函數聲明為:
public void onReceivedSslError(WebView view, SslErrorHandler handler, android.net.http.SslError error) {
   handler.proceed();
}
  • WebView view:當前的WebView實例
  • SslErrorHandler handler:當前處理錯誤的Handler,它只有兩個函數SslErrorHandler.proceed()和SslErrorHandler.cancel(),SslErrorHandler.proceed()表示忽略錯誤繼續加載,SslErrorHandler.cancel()表示取消加載。在onReceivedSslError的默認實現中是使用的SslErrorHandler.cancel()來取消加載,所以一旦出來SSL錯誤,HTTPS網站就會被取消加載了,如果想忽略錯誤繼續加載就只有重寫onReceivedSslError,并在其中調用SslErrorHandler.proceed()
  • SslError error:當前的的錯誤對象,SslError包含了當前SSL錯誤的基本所有信息。
    如加載SSL出錯的網站((12306是通過Https協議來傳輸的,但是它的SSL證書是有問題的,所以我們就以12306網站為例))
mWebView.setWebViewClient(new WebViewClient(){  
    @Override  
    public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {  
//      一定要注釋掉!      
//      super.onReceivedSslError(view, handler, error);  
        handler.proceed();  
        Log.e(TAG,"sslError:"+error.toString());  
    }  
}); 
mWebView.loadUrl("https://www.12306.cn/"); 

注意:當出現SSL錯誤時,WebView默認是取消加載當前頁面,只有去掉onReceivedSslError的默認操作,然后添加SslErrorHandler.proceed()才能繼續加載出錯頁面當HTTPS傳輸出現SSL錯誤時,錯誤會只通過onReceivedSslError回調傳過來,而不會觸發onReceivedError回調的。

  • WebViewClient其余函數
/** 
 * 在加載頁面資源時會調用,每一個資源(比如圖片)的加載都會調用一次 
 */  
public void onLoadResource(WebView view, String url)   
 /** 
 *  (WebView發生改變時調用)  
 *  可以參考http://www.it1352.com/191180.html的用法 
 */  
 public void onScaleChanged(WebView view, float oldScale, float newScale)  
 /** 
 * 重寫此方法才能夠處理在瀏覽器中的按鍵事件。 
 * 是否讓主程序同步處理Key Event事件,如過濾菜單快捷鍵的Key Event事件。 
 * 如果返回true,WebView不會處理Key Event, 
 * 如果返回false,Key Event總是由WebView處理。默認:false 
 */  
public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event)  
 /** 
 * 是否重發POST請求數據,默認不重發。 
 */  
onFormResubmission(WebView view, Message dontResend, Message resend)  
 /** 
 * 更新訪問歷史 
 */  
doUpdateVisitedHistory(WebView view, String url, boolean isReload)  
 /** 
 * 通知主程序輸入事件不是由WebView調用。是否讓主程序處理WebView未處理的Input Event。 
 * 除了系統按鍵,WebView總是消耗掉輸入事件或shouldOverrideKeyEvent返回true。 
 * 該方法由event 分發異步調用。注意:如果事件為MotionEvent,則事件的生命周期只存在方法調用過程中, 
 * 如果WebViewClient想要使用這個Event,則需要復制Event對象。 
 */  
onUnhandledInputEvent(WebView view, InputEvent event)  
 /** 
 * 通知主程序執行了自動登錄請求。 
 */  
onReceivedLoginRequest(WebView view, String realm, String account, String args)  
/** 
 * 通知主程序:WebView接收HTTP認證請求,主程序可以使用HttpAuthHandler為請求設置WebView響應。默認取消請求。 
 */  
onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm)  
/** 
 * 通知主程序處理SSL客戶端認證請求。如果需要提供密鑰,主程序負責顯示UI界面。 
 * 有三個響應方法:proceed(), cancel() 和 ignore()。 
 * 如果調用proceed()和cancel(),webview將會記住response, 
 * 對相同的host和port地址不再調用onReceivedClientCertRequest方法。 
 * 如果調用ignore()方法,webview則不會記住response。該方法在UI線程中執行, 
 * 在回調期間,連接被掛起。默認cancel(),即無客戶端認證 
 */  
onReceivedClientCertRequest(WebView view, ClientCertRequest request)

三、WebChromeClient

主要輔助WebView處理Javascript的對話框、網站Logo、網站title、load進度等處理。

  1. onCloseWindow(WebView window)
    通知主程序關閉WebView,并從View中移除,WebCore停止任何的進行中的加載和JS功能。
  • onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg)
    請求主程序創建一個新的Window,如果主程序接收請求,返回true并創建一個新的WebView來裝載Window,然后添加到View中,發送帶有創建的WebView作為參數的resultMsg的給Target。如果主程序拒絕接收請求,則方法返回false。默認不做任何處理,返回false
  • onJsAlert(WebView view, String url, String message,JsResult result)
    當網頁調用alert()來彈出alert彈出框前回調,用以攔截alert()函數
    注意:
  • 如果需要使網頁中的confrim()、alert()、prompt()函數生效,需要設置WebChromeClient!
  • 在使用onJsAlert來攔截alert對話框時,如果不需要再彈出alert對話框,一定要return true;在return false以后,會依然調用系統的默認處理來彈出對話框的
  • 如果我們return true,則需要在處理完成以后調用JsResult.confirm()或者JsResult.cancel()來告訴WebView我們點中哪個按鈕來取消程序對話框。否則再次點擊按鈕將會失敗
  • onJsPrompt(WebView view, String url, String message,String defaultValue, JsPromptResult result)
    當網頁調用prompt()來彈出prompt彈出框前回調,用以攔截prompt()函數
  • onJsConfirm(WebView view, String url, String message,JsResult result)
    當網頁調用confirm()來彈出confirm彈出框前回調,用以攔截confirm()函數
  • onProgressChanged(WebView view, int newProgress)
    通知程序當前頁面加載進度
      WebView view:當前WebView實例
      int newProgress:當前的加載進度,值從0到100
  • onReceivedIcon(WebView view, Bitmap icon)
    通知當前頁面網站新圖標
  • onReceivedTitle(WebView view, String title)
    通知頁面標題變化
  • onShowCustomView

WebView只是用來處理一些html的頁面內容,只用WebViewClient就行了,如果需要更豐富的處理效果,比如JS、進度條等,就要用到WebChromeClient。

四、CookieSync

  1. CookieManager
    CookieManager是用來管理Cookie的,主要來管理cookie相關,提供如下API:
  • setAcceptCookie()
  • setCookie()
  • getCookie(String url);
  • removeSessionCookies();
  • hasCookies()
  • removeAllCookie()
  1. CookieSyncManager
    CookieSyncManagerl繼承WebSyncManager,來管理同步cookie相關,主要有以下API:
  • resetSync()
  • stopSync()
  • sync()
  • syncFromRamToFlash()
  • checkInstanceIsAllowed()

實現cookie同步:

CookieManager cookieManager = CookieManager.getInstance(); 
// 接受服務器
cookie cookieManager.setAcceptCookie(true); 
//移除之前的cookie 
cookieManager.removeSessionCookie(); 
// 注入cookies 
List<String> cookies = getCookies(customCookies); 
for (String cookie : cookies) {
 cookieManager.setCookie(uri.getHost(), cookie);
 } 
// 同步cookie 
CookieSyncManager.getInstance().sync();

五、緩存模式

webview緩存模式有5種,具體方式:

  • LOAD_CACHE_ONLY: 不使用網絡,只讀取本地緩存數據
  • LOAD_DEFAULT: 根據cache-control決定是否從網絡上取數據。
  • LOAD_CACHE_NORMAL: API level 17中已經廢棄, 從API level 11開始作用同LOAD_DEFAULT模式
  • LOAD_NO_CACHE: 不使用緩存,只從網絡獲取數據.
  • LOAD_CACHE_ELSE_NETWORK,只要本地有,無論是否過期,或者no-cache,都使用緩存中的數據。

六、Native與JS相互調用

該模塊是針對JsBridge進行封裝的功能介紹:

  1. Java調用js代碼
public void registerHandler(final String handlerName, final JsHandler handler) {
     this.mWebView.registerHandler(handlerName, new BridgeHandler() {
         public void handler(String data, CallBackFunction function) {
             if(handler != null) {
                handler.OnHandler(handlerName, data, function);
              // data實際就是js返回的jsonString數據,本案例為了統一處理,data統一采用{code,msg ,data} 形式,
             }
        }
    });
}
 //先注冊回調js的方法
        mProgressBarWebView.registerHandlers(mHandlers, new JsHandler() {
            @Override
            public void OnHandler(String handlerName, String responseData, CallBackFunction function) {
              if (handlerName.equals("callNative")) {
         ToastUitl.showShort(responseData);
           function.onCallBack("我在XXXX");
    } else  if (handlerName.equals("callJs")) {
                    ToastUitl.showShort(responseData);
                    // 想調用你的方法:
                    function.onCallBack("好的 這是圖片地址 :xxxxxxx");
                } if (handlerName.equals("open")) {
                    mfunction = function;
                    pickFile();
                }
            }
        });
// 調用js
        mProgressBarWebView.callHandler("callNative", "hello H5, 我是java", new JavaCallHandler() {
            @Override
            public void OnHandler(String handlerName, String jsResponseData) {
                ToastUitl.showShort("h5返回的數據:" + jsResponseData);
            }
        });
  1. js調用Native
public void callHandler(final String handlerName, String javaData, final JavaCallHandler handler) {
    this.mWebView.callHandler(handlerName, javaData, new CallBackFunction() {
        public void onCallBack(String data) {
            if(handler != null) {
                handler.OnHandler(handlerName, data);
            }
        }
    });
}
// 注冊一個"callNative"函數,用來接收java層的數據
            bridge.registerHandler("callNative", function(data, responseCallback) {
                document.getElementById("show").innerHTML = ("data from Java: = " + data);
                var responseData = "hello java !  我要你的地址!";

                alert('JS say:'+  responseData);

                // response層
                responseCallback(responseData);
            });
function onUrl() {
            var data = "我要你一個url";
            //call native method
            window.WebViewJavascriptBridge.callHandler(
                'callJs'
                , {'param': data }
                , function(responseData) {
                 alert('Js 收到你的地址:'+ responseData);
                }
            );
        }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容