WebView和JS相互通信
WebView调用JS函数
通用方式,不能获取JS函数的返回值:
webView.loadUrl("javascript:alert('hello world');");
Android 4.4.及以上系统的WebView专用方式,可以获取JS函数的返回值:
webView.evaluateJavascript("javascript:add(2,3)", new ValueCallback<String>() { @Override public void onReceiveValue(String value) { Log.i("tag",value); } });
JS调用Native函数
首先,需要通过WebView提供的函数注册JS对象
public class AndroidJSInterface { @JavascriptInterface //注意,所有可被JS调用的函数,一定要加上@JavascriptInterface,否则JS无法调用 public String handler(String action, String value){ return ""; } }
webView.addJavascriptInterface(new AndroidJSInterface(),"Android");
JS调用Native提供的handler函数的代码示例如下:
window.Android.handler("sayHello","hello world");
其中Android对象就是我们通过addJavascriptInterface()函数注册的JS对象
JS调用Native对象,支持有返回值的函数和没有返回值的函数
JS调用Native函数的时候,建议通过JSON数据来传值,不然的话,可能会出现调用失败的情况
比如:
window.Android.handler("sayHello",JSON.stringify(obj));
当JS不再使用Native对象时,可以把Native对象注销掉
webView.removeJavascriptInterface("Android");
WebView加载URL和HTML字符串的方法
WebView加载指定的url
不带http header
webView.loadUrl(mUrl);
带http header
Map<String,String> httpHeaders=new LinkedHashMap<>(); httpHeaders.put("userName","kgdwbb"); webView.loadUrl(mUrl,httpHeaders);
WebView加载html片段
webView.loadData("<h1>title</h1>","text/html; charset=utf-8", null);
这里有一个问题,就是当webView.loadData()函数第三个参数传入页面的字符编码的时候,不起作用,可能是这个函数本身的问题
我的解决方法就是在第二个参数里面传入页面的字符编码,比如charset=utf-8,如果不显示指定页面的字符编码,在显示中文的时候可能会出现乱码的情况
WebView 发送POST请求
代码示例:
webView.loadUrl(mUrl); byte[]data="hello world".getBytes(); webView.postUrl(mUrl,data);
WebView Cookie设置
WebView cookie设置一定要在LoadUrl函数之前调用,也就是说在WebView发送网络请求之前设置。
Android通过CookieManager类来设置Cookie,通过CookieSyncManager类把CookieManager类设置的Cookie数据保存到应用程序/data/data/databases/目录下的webviewCookiesChromium.db数据库的cookies表中,这个数据库属于全局公共数据库,对这个数据库的操作会影响所有WebView,所以在使用完这个数据库之后,一定要记得清除设置的Cookie数据,以免对其它使用WebView的APP造成影响。
下面是设置WebViewCookie的代码示例:
public static void synCookies(Context context, String url,Stringcookie) {
CookieManager cookieManager =CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
cookieManager.setCookie(url, cookie);
CookieSyncManager.getInstance().sync();
}
其中cookie是键值对类型的字符串,比如cookie="userName=kgdwbb"
下面是清除WebViewCookie的代码示例:
public static void removeCookie() { CookieManager cookieManager = CookieManager.getInstance(); cookieManager.removeAllCookie(); CookieSyncManager.getInstance().sync(); }
WebView缓存设置
设置WebView缓存的代码示例如下:
WebSettings webSettings=webView.getSettings(); webSettings.setAppCacheEnabled(true); webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
清除WebView缓存的代码如下:
webView.clearCache(true);
WebView历史堆栈
代码示例如下:
后退
if(webView.canGoBack()){ webView.goBack(); return; }
前进
if(webView.canGoForward()){ webView.goForward(); }
前进或后退
int steps=2; if(webView.canGoBackOrForward(steps)){ webView.goBackOrForward(steps); }
对WebView的堆栈列表进行操作
int backForwardListSize= webView.copyBackForwardList().getSize(); for(int i=0;i<backForwardListSize;i++){ WebHistoryItem item=webView.copyBackForwardList().getItemAtIndex(i); Log.i("tag",item.getUrl()); }
清除WebView历史堆栈
webView.clearHistory();
WebView启用文件下载功能
示例代码如下:
webView.setDownloadListener(new DownloadListener() { @Override public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimeType,long contentLength) { //在这里写真正的文件下载的代码,WebView是不会自动下载文件的 } });
onDownloadStart函数参数说明:
url:就是服务器上文件的url地址或者文件流的地址
userAgent:就是用户浏览器的默认userAgent头
contentDisposition:url对应的文件类型,当url是服务器上的一个真实文件时,这个值为空,当url是服务器上可以访问的文件流时,这个值就会包涵这个文件流的一些基本信息,比如文件名等
mimeType:文件或文件流的类型,比如二进制文件流的mimeType是application/octet-stream
当WebView需要下载网页里面的文件时,会调用这个文件下载接口,我们只需要处理这个接口,就可以实现文件下载功能。文件下载功能需要我们自己来实现,WebView默认是不提供文件下载功能的。
让WebView支持文件选择
WebView默认不支持表单的file标签,如果想让WebView支持表单的file标签,我们可以这样做:
webView.setWebChromeClient(new WebChromeClient(){ //当WebView需要显示文件选择器时,回调这个函数,我们可以重写这个函数,加载我们自定义的文件选择器 @Override public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) { return super.onShowFileChooser(webView, filePathCallback, fileChooserParams); } });
自定义WebView的错误页面
当WebView在加载网页的时候如果出现网络错误或者指定的网页不存在的时候,就会显示默认的错误页面,如果我们想重写这个默认的错误页面,可以用下面的方法:
webView.setWebViewClient(new WebViewClient(){ //当WebView发生任何请求错误的时候,都会回调这个函数 @Override public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { super.onReceivedError(view, request, error);
view.loadUrl("file:///android_asset/error.html");//加载自定义的错误页面,前提是这个错码页面一定要存在assets文件夹下面 } });
忽略WebView的SSL证书错误
当WebView在加载https网页的时候,如果网页存在SSL证书错误,比如12306网站的证书错误,我们可以忽略网页的错误证书,继续执行下面的网页,我们可以这样做:
webView.setWebViewClient(new WebViewClient(){ //当接收到服务器返回的SSL错误时,回调这个函数 @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { super.onReceivedSslError(view, handler, error); handler.proceed();//忽略SSL证书错误,继续执行 } });
拦截WebView的所有网络请求
要拦截WebView的网络请求,我们可以这样做:
webView.setWebViewClient(new WebViewClient(){ //当WebView需要进行任何网络请求时,都会调用这个函数,我们可以拦截这个函数,做相应的处理 //比如加载缓存的图片等,这个函数已经被Android 4.4以上的系统废弃,建议在Android 4.4.及以上的系统使用新的函数 @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { return super.shouldInterceptRequest(view, url); } //当WebView需要进行任何网络请求时,都会调用这个函数,我们可以拦截这个函数,做相应的处理 //比如加载缓存的图片等,这个函数属于Android 4.4及以上的WebView提供的新的函数 @Override public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { WebResourceResponse response=super.shouldInterceptRequest(view,request); return response; } });
重写WebView H5 video标签的默认属性
要想让WebView里面的所有H5 video标签都显示默认的加载进度和视频的默认图片,我们可以这样做:
webView.setWebChromeClient(new WebChromeClient(){ //获取video加载时默认显示的第一张图片 @Override public Bitmap getDefaultVideoPoster() { return super.getDefaultVideoPoster(); } //获取视频加载进度View,所以我们可以在这里重写默认的video加载进度 @Override public View getVideoLoadingProgressView() { return super.getVideoLoadingProgressView(); } });
捕捉WebView网页输出的所有日志
要监控网页输出的日志,我们可以这样做:
webView.setWebChromeClient(new WebChromeClient(){ //当JS输出日志时,回调这个函数 @Override public boolean onConsoleMessage(ConsoleMessage consoleMessage) { return super.onConsoleMessage(consoleMessage); } });
WebView JS计时器设置
默认情况下,WebView的计时器在APP进入后台的时候还是会执行的。如果想让APP进入后台的时候暂时WebView的计时器,在APP进入前台的时候恢复WebView的计时器,代码如下:
@Override protected void onPause() { super.onPause(); //当Activity页面进入后台的时候,暂停WebView的计时器,这样WebView页面的JS计时器就会被暂停 if(webView!=null) webView.pauseTimers(); } @Override protected void onResume() { super.onResume(); //当Activity页面进入前台的时候,恢复WebView的计时器,这样WebView页面的JS计时器就会被恢复执行 if(webView!=null) webView.resumeTimers(); }
WebView资源释放
默认情况下,在Activity销毁的时候,WebView是不会自动释放它占用的系统资源的,我们需要手动进行释放,否则会造成严重的内存泄露,
下面是释放WebView占用内存的代码:
@Override protected void onDestroy() { //释放WebView资源,否则会造成内存泄露 if(webView!=null){ webView.destroy(); webView=null; } super.onDestroy(); }
WebView常用设置功能汇总
WebView启用JS支持
WebSettingswebSettings=webView.getSettings();
webSettings.setJavaScriptEnabled(true);
WebView启用双指缩放功能
webSettings.setDisplayZoomControls(true);//true显示缩放控件,false隐藏缩放控件
webSettings.setBuiltInZoomControls(true);
webSettings.setSupportZoom(true);
WebView启用https支持
在android 5.0之前,WebView是可以在http环境中直接访问https资源和服务的,但是在android 5.0及以后,要想在http环境访问https资源和服务,就需求在WebView中开启对https的支持,代码如下:
WebSettingswebSettings=webView.getSettings();
if(Build.VERSION.SDK_INT>Build.VERSION_CODES.LOLLIPOP){
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
WebView启用本地存储功能
默认情况下,是无法使用JS的localStorage,sessionStorage对象访问本地存储的,下面是开启WebView本地存储功能的代码:
WebSettings webSettings=webView.getSettings();
webSettings.setDomStorageEnabled(true);
WebView启用LBS定位功能
WebSettings webSettings=webView.getSettings();
webSettings.setGeolocationEnabled(true);
WebView调试设置
这个API只支持Android 4.4及以上的系统,Android 4.4以下的系统默认开启了WebView的调用功能。
代码如下:
if(Build.VERSION.SDK_INT>Build.VERSION_CODES.KITKAT){ webView.setWebContentsDebuggingEnabled(true); }
我们可以借助Chrome浏览器来对WebView进行调试
WebSettings类解析
WebSettings webSettings=webView.getSettings(); //开启WebView对JS脚本的支持 webSettings.setJavaScriptEnabled(true); //设置允许在http环境中访问https资源和服务 if(Build.VERSION.SDK_INT>Build.VERSION_CODES.LOLLIPOP){ webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); } //设置默认的网页编码 webSettings.setDefaultTextEncodingName("utf-8"); //设置JS是否可以打开WebView新窗口 webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //设置WebView是否支持多窗口,为true,需要重写WebChromeClient#onCreateWindow函数 webSettings.setSupportMultipleWindows(true); //禁止WebView加载网络图片 webSettings.setLoadsImagesAutomatically(false); webSettings.setBlockNetworkImage(true); //显示WebView提供的缩放控件 webSettings.setDisplayZoomControls(true);//true显示缩放控件,false隐藏缩放控件 webSettings.setBuiltInZoomControls(true); webSettings.setSupportZoom(true); //开启WebView对数据库的支持 webSettings.setDatabaseEnabled(true); //开启WebView的Storage功能,这样JS的localStorage,sessionStorage对象才可以使用 webSettings.setDomStorageEnabled(true); //打开WebView的LBS功能,这样JS的geolocation对象才可以使用 webSettings.setGeolocationEnabled(true); //设置WebView是否自动保存表单数据 webSettings.setSaveFormData(true); //设置WebView的默认userAgent字符串 webSettings.setUserAgentString(""); //设置是否打开WebView的桌面模式,true是桌面模式,false是移动模式 webSettings.setUseWideViewPort(false); //设置WebView的默认字体,可以通过这个函数,改变WebView的默认字体 webSettings.setStandardFontFamily(""); //设置WebView默认字体的大小 webSettings.setDefaultFontSize(20); //设置WebView支持的最小字体大小 webSettings.setMinimumFontSize(12); //设置页面的文本缩放倍数 webSettings.setTextZoom(2);
WebViewClient类解析
webView.setWebViewClient(new WebViewClient(){ //当WebView需要加载新的url的时候会调用这个函数,在这个函数里面我们不能调用任何WebView的LoadUrl函数, //否则,会出现页面被多次加载,造成JS location.replace函数失效,还有看起来像302问题导致的WebView需要 //多次点击才能返回的BUG @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { return super.shouldOverrideUrlLoading(view, url); } //WebView页面已经开始加载指定的url @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); } //WebView加载完指定的url @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); Log.i("hello",url); view.loadUrl("javascript:window.android.handler('<head>'+document.getElementsByTagName('html')[0].innerHTML+'</head>');"); } //当WebView需要进行任何网络请求时,都会调用这个函数,我们可以拦截这个函数,做相应的处理 //比如加载缓存的图片等,这个函数已经被Android 4.4以上的系统废弃,建议在Android 4.4.及以上的系统使用新的函数 @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { return super.shouldInterceptRequest(view, url); } //当WebView需要进行任何网络请求时,都会调用这个函数,我们可以拦截这个函数,做相应的处理 //比如加载缓存的图片等,这个函数属于Android 4.4及以上的WebView提供的新的函数 @Override public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { WebResourceResponse response=super.shouldInterceptRequest(view,request); return response; } //当WebView发生任何请求错误的时候,都会回调这个函数 @Override public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { super.onReceivedError(view, request, error); view.loadUrl("file:///android_asset/error.html");//加载自定义的错误页面 } //当WebView接收到服务器错误时,回调这个函数 @Override public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) { super.onReceivedHttpError(view, request, errorResponse); } //当WebView表单重新提交时,回调这个函数 @Override public void onFormResubmission(WebView view, Message dontResend, Message resend) { super.onFormResubmission(view, dontResend, resend); } //当WebView需要更新它的url访问数据库的时候,回调这个函数 @Override public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) { super.doUpdateVisitedHistory(view, url, isReload); } //当接收到服务器返回的SSL错误时,回调这个函数 @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { super.onReceivedSslError(view, handler, error); handler.proceed();//忽略SSL证书错误,继续执行 } //当WebView访问https服务器时,需要客户端提供对应的SSL证书时,回调这个函数 @Override public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) { super.onReceivedClientCertRequest(view, request); } //当WebView接收到服务器的授权请求时,回调这个函数 @Override public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) { super.onReceivedHttpAuthRequest(view, handler, host, realm); } //当发生键盘事件时,回调这个函数 @Override public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) { return super.shouldOverrideKeyEvent(view, event); } //当WebView的缩放值发生改变的时候,回调这个函数 @Override public void onScaleChanged(WebView view, float oldScale, float newScale) { super.onScaleChanged(view, oldScale, newScale); } });
WebChromeClient类解析
webView.setWebChromeClient(new WebChromeClient(){ //当WebView加载进度改变时,回调这个函数 @Override public void onProgressChanged(WebView view, int newProgress) { super.onProgressChanged(view, newProgress); } //当通过JS改变html文档的标题时,回调这个函数 @Override public void onReceivedTitle(WebView view, String title) { super.onReceivedTitle(view, title); } //当WebView开启多窗口模式时,需要处理这个函数,这位JS才可以打开多个WebView窗口 @Override public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) { return super.onCreateWindow(view, isDialog, isUserGesture, resultMsg); } //当JS关闭指定的WebView窗口时,回调这个函数 @Override public void onCloseWindow(WebView window) { super.onCloseWindow(window); } //当JS调用alert函数时,回调这个函数 @Override public boolean onJsAlert(WebView view, String url, String message, JsResult result) { return super.onJsAlert(view, url, message, result); } //当JS调用confirm函数时,回调这个函数 @Override public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { return super.onJsConfirm(view, url, message, result); } //当JS调用prompt函数时,回调这个函数 @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { return super.onJsPrompt(view, url, message, defaultValue, result); } //当JS使用geolocation API访问当前位置时,回调这个函数 @Override public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) { super.onGeolocationPermissionsShowPrompt(origin, callback); } //当JS取消geolocation API访问的时候 @Override public void onGeolocationPermissionsHidePrompt() { super.onGeolocationPermissionsHidePrompt(); } //当JS输出日志时,回调这个函数 @Override public boolean onConsoleMessage(ConsoleMessage consoleMessage) { return super.onConsoleMessage(consoleMessage); } //获取video加载时默认显示的第一张图片 @Override public Bitmap getDefaultVideoPoster() { return super.getDefaultVideoPoster(); } //获取视频加载进度View,所以我们可以在这里重写默认的video加载进度 @Override public View getVideoLoadingProgressView() { return super.getVideoLoadingProgressView(); } //当JS获取访问历史记录时,回调这个函数 @Override public void getVisitedHistory(ValueCallback<String[]> callback) { super.getVisitedHistory(callback); } //当WebView需要显示文件选择器时,回调这个函数,我们可以重写这个函数,加载我们自定义的文件选择器 @Override public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) { return super.onShowFileChooser(webView, filePathCallback, fileChooserParams); } });
WebView常用问题汇总
Android 7.0及以上系统WebView长按崩溃的解决方法
/**
* Android 7.0 WebView长按会崩溃,解决长按崩溃的办法,就是屏蔽掉Android7.0 WebView的长按事件
*/
privatevoid setWebViewLongClickListener(){
webView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if(Build.VERSION.SDK_INT> Build.VERSION_CODES.N){//android 7.0 Nougat
return true;
}
return false;
}
});
}