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);
                }
            );
        }

推荐阅读更多精彩内容