一、基本情况介绍
Android WebView在Android平台上是一个特殊的View, 基于webkit引擎、展现web页面的控件。
WebView内部实现是采用渲染引擎来展示view的内容,提供网页前进后退,网页放大,缩小,搜索。
二、常用接口介绍
WebView有几个比较重要的辅助类。
WebSetting:用来配置WebView的一些基本参数
WebViewClient:主要帮助WebView处理各种通知、请求事件的
WebChromeClient:主要辅助WebView处理Javascript的对话框、网站图标、网站title、加载进度等
1.通过WebSetting,设置一些基本参数
//声明WebSettings子类
WebSettings webSettings = webView.getSettings();
//如果访问的页面中要与Javascript交互,则webview必须设置支持Javascript
webSettings.setJavaScriptEnabled(true);
// 定位(location)
settings.setGeolocationEnabled(true);
// 默认文本编码,默认值 "UTF-8"
settings.setDefaultTextEncodingName("UTF-8");
settings.setDefaultFontSize(16); // 默认文字尺寸,默认值16,取值范围1-72
settings.setDefaultFixedFontSize(16); // 默认等宽字体尺寸,默认值16
settings.setMinimumFontSize(8); // 最小文字尺寸,默认值 8
settings.setMinimumLogicalFontSize(8); // 最小文字逻辑尺寸,默认值 8
settings.setTextZoom(100); // 文字缩放百分比,默认值 100
// 字体
settings.setStandardFontFamily("sans-serif"); // 标准字体,默认值 "sans-serif"
settings.setSerifFontFamily("serif"); // 衬线字体,默认值 "serif"
settings.setSansSerifFontFamily("sans-serif"); // 无衬线字体,默认值 "sans-serif"
settings.setFixedFontFamily("monospace"); // 等宽字体,默认值 "monospace"
settings.setCursiveFontFamily("cursive"); // 手写体(草书),默认值 "cursive"
settings.setFantasyFontFamily("fantasy"); // 幻想体,默认值 "fantasy"
//设置自适应屏幕,两者合用
webSettings.setUseWideViewPort(true); //将图片调整到适合webview的大小
webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小
//缩放操作
webSettings.setSupportZoom(true); //支持缩放,默认为true。是下面那个的前提。
webSettings.setBuiltInZoomControls(true); //设置内置的缩放控件。若为false,则该WebView不可缩放
webSettings.setDisplayZoomControls(false); //隐藏原生的缩放控件
//其他细节操作
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //关闭webview中缓存
webSettings.setAllowFileAccess(true); //设置可以访问文件
webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口
webSettings.setLoadsImagesAutomatically(true); //支持自动加载图片
2.WebViewClient回调接口
//在网页开始加载时回调 。
public void onPageStarted(WebView view, String url, Bitmap favicon) {
}
//在网页结束加载时回调。
public void onPageFinished(WebView view, String url) {
}
//当加载的网页需要重定向,或者超链接在加载前都会回调此函数,可以在这里拦截一些url的加载。
//return true 就会拦截掉;return false 就是交由webview处理,url会继续加载。
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
//WebView即将加载指定的url资源时回调。
public void onLoadResource(WebView view, String url) {
}
//访问指定的url发生错误时回调,我们可以在这里做错误处理,比如无网络的错误页面。
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
}
//通知可以更新访问记录,意味着当前的访问url已经生效并在内核当中生成了访问记录。
public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) {
}
//当网页加载资源过程中发现SSL错误时回调。
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
}
3.WebChromeClient回调接口
//当前WebView的加载url的进度变化时回调。
public void onProgressChanged(WebView view, int newProgress) {
}
//获取到当前url页面的title时回调。
public void onReceivedTitle(WebView view, String title) {
}
//获取到当前url页面的icon标识时回调
public void onReceivedIcon(WebView view, Bitmap icon) {
}
//需要显示JavaScript警告的dialog时回调。
public boolean onJsAlert(WebView view, String url, String message,
JsResult result) {
return false;
}
//需要展示一个确认的对话框时回调。
public boolean onJsConfirm(WebView view, String url, String message,
JsResult result) {
return false;
}
//需要展示一个含输入框的对话框时回调。
public boolean onJsPrompt(WebView view, String url, String message,
String defaultValue, JsPromptResult result) {
return false;
}
//需要显示文件的选择器时回调(android 5.0及后)
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
FileChooserParams fileChooserParams) {
return false;
}
//需要显示文件的选择器时回调(android 5.0前)
public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture) {
uploadFile.onReceiveValue(null);
}
4.WebView的常用接口
//加载网页
public void loadUrl(String url); // 加载URL指定的网页
public void loadUrl(String url, Map<String, String> additionalHttpHeaders); // 携带http headers加载URL指定的网页
public void reload(); // 重新加载当前网页
//获取页面信息相关
public String getUrl(); // 获取当前页面的URL
public String getOriginalUrl(); // 获取当前页面的原始URL
public String getTitle(); // 获取当前页面的标题
public Bitmap getFavicon(); // 获取当前页面的favicon
public int getProgress(); // 获取当前页面的加载进度
//前进后退
public boolean canGoBack(); // 是否可后退
public boolean canGoForward(); // 是否可前进
public void goBack(); // 后退一页
public void goForward(); // 前进一页
public void clearHistory();// 清除当前webview访问的历史记录
public WebBackForwardList copyBackForwardList(); // 复制一份BackForwardList
//Javascript
public void addJavascriptInterface(Object object, String name); // 注入Javascript对象
public void removeJavascriptInterface(String name); // 移除已注入的Javascript对象,下次加载或刷新页面时生效
// 对传入的JS表达式求值,通过resultCallback返回结果
// 此函数添加于API19,必须在UI线程中调用,回调也将在UI线程
public void evaluateJavascript(String script, ValueCallback<String> resultCallback)
5.简单使用
webview.getSettings().setJavaScriptEnabled(true);
webview.setWebChromeClient(new WebChromeClient() {
public void onProgressChanged(WebView view, int progress) {
// Activities and WebViews measure progress with different scales.
// The progress meter will automatically disappear when we reach 100%
}
});
webview.setWebViewClient(new WebViewClient() {
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();
}
});
webview.loadUrl("https://developer.android.com/");
三、Hybrid的使用
Hybrid混合开发前两年非常流行,因其无需升级客户端版本就可实现快速更新功能、跨平台等特性,使得在一些电商平台,比如说很多电商平台,淘宝、京东、聚划算,还有一些应用的活动页面等。
WebView比较灵活,不需要升级客户端,只需要修改网页代码即可。因此项目中经常变化的页面可以用WebView这种方式去加载网页。
Hybrid开发模式有着开发快、更新快、跨平台等诸多有点,但是由于需要依赖于WebView来加载,就会存在一些性能和体验的问题,相较于原生的View,这一块都要比原生的差很多。后来推出的React Native、Weex等基于web开发,但是渲染为Native的方式,目前更加受到追捧。
美团页面:
饿了么:
京东:
1.基本使用方法
(1)设置WebView属性
webSettings.setJavaScriptEnabled(true);
必须设置为true,使webview支持JS。
(2)定义好桥接模块的接口
Android与JS可以通过WebView互相调用其方法。
2.JS去调用Android的代码:
首先需要声明方法,然后加上@JavascriptInterface
public class AndroidJsBridgeJavaScriptInterface{
// 定义JS调用的方法,并加入@JavascriptInterface注解
@JavascriptInterface
public void testMethod(String msg) {
Toast.makeText(AppContextUtil.getAppContext(),"JS调用了Android的testMethod方法", Toast.LENGTH_SHORT).show();
}
}
向webview中注入桥接对象
//AndroidJsBridgeJavaScriptInterface类对象映射到js的AndroidJsInterface对象
webView.addJavascriptInterface(new AndroidJsBridgeJavaScriptInterface(), "AndroidJsInterface");
使用chrome://inspect/#devices(墙)可以直接查看到注入的对象
在Js中调用Android模块注入的方法
首先编写JS:TestJsAndroid.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>TestJs</title>
<style>
.button {
background-color: #4CAF50;
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
}
</style>
<script>
function callAndroid(){
//由于对象映射,所以调用AndroidJsInterface对象等于调用Android映射的对象
AndroidJsInterface.testMethod("js调用了android中的testMethod方法");
}
</script>
</head>
<body>
点击按钮则调用callAndroid函数
<br />
<button type="button" class="button" οnclick="callAndroid()">callAndroid</button>
</body>
</html>
加载Js
webView.loadUrl("file:///android_asset/TestJsAndroid.html");//目前js放在assets目录下,也可以部署到服务器,直接加载url
除了上面的方式,还可以通过自定义协议,在WebViewClient 的shouldOverrideUrlLoading ()方法回调拦截url或者通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截,然后调用相应的方法。
3.Android去调用JS的代码
首先编写JS:TestAndroidJs.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>TestAndroidJs</title>
<script>
//Android需要调用的方法
function callJSMethod(){
alert("Android中调用了JS的方法");
}
</script>
</head>
</html>
Android中调用
webView.loadUrl("javascript:callJSMethod()");
注意:
调用JS方法可以有两种方式
一:直接使用loadUrl方法;
二:使用evaluateJavascript方法
webView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
@Override public void onReceiveValue(String value) {
//此处为 js 返回的结果
}
});
}
两者的区别是:
evaluateJavascript是Android 4.4后开始支持,异步执行,效率更高,带有返回值
loadUrl不需要考虑版本问题,但是同步执行,性能较差
四.Hybrid的一个扩展应用场景
在第三方资讯详情页中,使用自己的评论模块和相关推荐、广告等,提高资讯使用的体验,增加广告的收入。
基本的流程和上面Hybrid的方式差不多,不同的有两点:
(1)需要注意js注入的时机,需要在资讯文章加载完成后注入
即:WebViewClient的onPageFinished方法中注入
(2)Js是需要在资讯文章的基础上拼接
javascript:{
$("body").append("<div style='position:fixed; bottom:15px'><b>这个是添加的内容</b></div>");
function myJsFunction(data) {
alert("myJsFunction:" + data);
}
}
五.释放
WebView使用后,释放不当,会导致内存泄漏
ViewGroup view = (ViewGroup) mWebView.getParent();//先从父容器中移除
view.removeView(mWebView);
mWebView.setWebViewClient(null); //把设置的接口置为空
mWebView.setWebChromeClient(null);
mWebView.destroy(); //调用其destroy方法,然后置空
mWebView = null;