Android中WebView的使用指南:
参考文献:
Carson_Ho的Android:这是一份全面 & 详细的Webview使用攻略
本文知识点:
- WebView的介绍
- WebView的基本使用
- WebView的一些开发中常用的API
- WebView中一些常见的案例分析及实现
1.WebView的介绍
相信做Android的基本上都用过WebView,不论是加载网页还是处理本地内容,基本上都或多或少的使用过WebView。那么WebView能为我们带来什么呢?其实就相当于一个内置的浏览器,我们可以使用它完成一下操作:
1.WebView可以加载网页
2.WebView可以加载html片段
3.WebView可以加载本地html
4.WebView与原生App进行交互
以上种种都是WebView能帮我们完成的内容,所以现在有很多App就采用混合开发。这样使得我们使用WebView的场景越来越多,所以这里总结一下关于WebView的一些内容。
2.WebView的基本使用
其实如果你只用WebView加载一个网页的话,还是很好上手的。基本上就三步:
- 在AndroidManifest注册网络权限:这个是必须的,切记切记!!!
- 加载相应的网页资源
webView.loadUrl(url);
但是这里虽然加载的是一个url,但是你会发现他会直接蹦到系统自带的浏览器中去。作为强大的开发人员,怎么处理呢?当然是有办法的了。。。
- 设置相应的本APP内打开的方法;
webView.setWebViewClient(new WebViewClient() { @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { view.loadUrl(String.valueOf(request.getUrl())); return true; } @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } });
上面的代码有几点需要说明一下:
- shouldOverrideUrlLoading(WebView view, WebResourceRequest request) 这个方法是在5.0以上的版本上使用的,而shouldOverrideUrlLoading(WebView view, String url)这个方法是在5.0以下使用的,有了这两个方法,就能在自己的APP中打开相应的网页链接了。。。
上面就是WebView的简单实用,基本上就是展示一个网页,没有相应的交互问题。
3.WebView一些开中常用的API
加载类:
- loadUrl(string url) 加载url地址的方法,参数就是一个Url地址;这里有一个点需要注意一下,这个方法加载的地址可以是
- 网址 如 ->
http://www.baidu.com
- 手机本地html地址 如->
file:///android_asset/test.html
- app包中assect目录下的html文件 如 ->
content://com.android.htmlfileprovider/sdcard/test.html
- 网址 如 ->
- loadData(String data, String mimeType, String encoding) 加载html片段
- 参数1:html片段
- 参数2:类型
- 参数3:编码格式
状态类
- onResume() 当webView为活跃状态的时候回调此方法(获取焦点)
- onPause() 切换到后台的时候回调此方法(失去焦点)
其实上面这两个方法是和生命周期匹配的两个方法
- pauseTimers() 全局范围切换到后台降低cpu功耗时回调的方法
- resumeTimers() 恢复正常状态的时候回调的方法
上面这两个方法,是为了提高自己APP的性能的方法,主要是在生命周期的方法中调用的,也就是说在Activity的onResume()和onPause()中调用的(个人感觉啊。。。)
- destroy() 销毁WebView的方法(主要是在音视频的时候,在这里释放相应的WebView,这里也是对应相应生命周期的方法的,但是这里注意一点,在销毁前必须先移除WebView,因为WebView会持有相应Activity的上下文引用所以这里要使用其父布局调用相应的removeView(View view)的方法进行移除)
操作网页类
- canGoBack() 是否可以后退
- goBack() 回退
- canGoForward() 是否可以前进
- goForward() 前进网页
- **goBackOrForward(intsteps) ** 前进或者后退指定的位置,正数为前进/负数为后退
这里面涉及到的主要内容就是操作网页的前进和后退,主要应用场景是什么呢?试想一下,有的WebView是嵌到APP中的,一般这样的网页都是没有标题的,标题的交互留给APP处理返回的问题,就会用到上面相应的API了,基本上都是处理相应的返回问题
@Override public boolean onKeyDown(int keyCode, KeyEvent event) { if ((keyCode == KEYCODE_BACK) && mWb.canGoBack()) { mWb.goBack(); return true; } return super.onKeyDown(keyCode, event); } @Override public void onBackPressed() { if (mWb.canGoBack()) { mWb.goBack(); } else { super.onBackPressed(); } }
上面两段代码是一样的,但是这里有一个很重要的问题需要注意一下,很重要啊:
有些网页存在重定向的时候,那么canGoBack()返回的会一直是true,我这里使用的是百度的链接,因为百度重定向了,所以一直返回的是true,如果这里要是自己公司开发的Url的话,应该不会出现这种情况!这里提供一种解决办法
@Override public void goBack() { WebBackForwardList mWebBackForwardList = copyBackForwardList(); // 判断当前历史列表是否最顶端,其实canGoBack已经判断过 if (mWebBackForwardList.getCurrentIndex() > 0) { // 获取历史列表 String historyUrl = mWebBackForwardList.getItemAtIndex( mWebBackForwardList.getCurrentIndex() - 1).getUrl(); if (historyUrl.equals("https://www.baidu.com/")) { //回到首页了 Activity activity = (Activity) mContext; activity.finish(); } } super.goBack(); }
上面的代码就是拷贝出所有前进的页面,然后判断因为这里重定向了,所以你getUrl()获取的网址永远是重定向那个,所以这里我就想直接把网址写死的话就能处理了,但是我觉得我的解决办法比较笨,还请大神们指教!!!
缓存类
- clearCache(true) 清除WebView产生的所有缓存
- clearHistory() 清除当前WebView访问的历史数据
- clearFormData() 清除表单数据
WebSettings类
这个类是对WebView进行管理和设置的
- getSettings() 获取WebSettings的
- setJavaScriptEnabled(boolean flag); 是否可以和js进行交互
- setPluginState(PluginState state) 是否支持插件,这里面传递的是一个枚举对象
- WebSettings.PluginState.OFF 不支持
- WebSettings.PluginState.ON 支持
- setUseWideViewPort(boolean use) 是否将图片调整到WebView的大小
- setLoadWithOverviewMode(boolean overview) 是否将WebView调整到屏幕大小
上面两个API一起使用!切记,因为只有这样才能让界面看上去不那么丑!
setSupportZoom(boolean support)支持缩放,默认为true。是下面这个API的前提。
setBuiltInZoomControls(boolean enabled) 设置内置的缩放控件。若为false,则该WebView不可缩放
setDisplayZoomControls(boolean enabled) 隐藏原生的缩放控件,这里可以自己在页面中实现。
-
setCacheMode(@CacheMode int mode) 设置缓存的模式
- WebSettings.LOAD_DEFAULT 默认的模式 根据cache-control决定是否从网络上取数据。
- WebSettings.LOAD_CACHE_ELSE_NETWORK 只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据
- WebSettings.LOAD_NO_CACHE 不使用缓存
- WebSettings.LOAD_CACHE_ONLY 不使用网络,只加载缓存
setAllowFileAccess(boolean allow) 设置可以访问app中assect中的文件
setJavaScriptCanOpenWindowsAutomatically(boolean flag) 支持通过JS打开新窗口
setLoadsImagesAutomatically(boolean flag) 支持自动加载图片
setDefaultTextEncodingName(String encoding) 设置编码格式
-
setRenderPriority(RenderPriority priority) 设置渲染的优先级,这里传入的是一个枚举
- NORMAL 正常
- HIGH 高
- LOW 低
WebViewClient类
处理各种通知和请求事件的控制类
- shouldOverrideUrlLoading(WebView view, WebResourceRequest request)
- shouldOverrideUrlLoading(WebView view, String url)
下面这个方法已经过时了,最新的是上面的那个方法,对于相应的url可以通过WebResourceRequest进行获取。重写这两个方法自己处理的话,可以不使用相应的浏览器进行打开,在本APP中打开,这也是一开始的时候为什么要重写这个方法。
- onPageStarted(WebView view, String url, Bitmap favicon) 加载WebView开始的时候回调的方法,一般都在这里添加相应的Loadding,与用户交互,告诉用户页面还没有加载完成;
- onPageFinished(WebView view, String url) WebView加载完成的时候回调的方法和上面那个方法是相呼应的,可以在这里关闭相应的对话框!
这里有一个问题注意一下,还是重定向的问题。如果这个网页重定向了,那么上面这两个方法会被多次调用!
onLoadResource(WebView view, String url) 这个方法会在每次加载资源的时候回调,如果图片很多的话,会被调用多次。后面这个url代表的是相应加载的地址。
onReceivedError(WebView view, int errorCode,String description, String failingUrl)
onReceivedError(WebView view, WebResourceRequest request, WebResourceError error)
上面这个方法已经过时了,可以根据相应的code进行判断。后面这个方法主要是通过WebResourceError的错误编码进行相应的判断,处理相应的错误!这个方法的主要作用是加载错误的回调。
- onReceivedError(WebView view, int errorCode,String description, String failingUrl)
- onReceivedError(WebView view, WebResourceRequest request, WebResourceError error)
这两个方法是处理相应的https的网络请求的(webView默认是不处理https请求的,页面会显示空白,使用如上面的方法进行处理)
@Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { super.onReceivedSslError(view, handler, error); handler.proceed(); } // 特别注意:5.1以上默认禁止了https和http混用,以下方式是开启 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { mWebView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); }
WebChromeClient类
如果你对控制类没有那么多要求的话,使用上面的控制类就可以了,但是如果你要求比较高的话,就要用到下面这个控制类了
- onProgressChanged(WebView view, int newProgress) 这个回调会实时的返回网页加载的总进度,最大值是100,可以根据当前值显示进度。
- onReceivedTitle(WebView view, String title) 获取当前加载网页的标题
- onJsAlert(WebView view, String url, String message, JsResult result) 如果这里返回true,那么js中弹出的警告对话框就由客户端处理;
- onJsAlert(WebView view, String url, String message, JsResult result) 如果这里返回true,那么js中弹出的确认对话框就由客户端处理;
- **onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) ** 如果这里返回true,那么js中弹出的提示对话框将由客户端进行处理。
4.WebView中一些常见的案例分析及实现
WebView显示进度
效果图由很多APP中由都嵌入WebView,不知道大家仔细看过没有,有的WebView在加载的时候顶部由一个进度条,提示用户加载的进度,其实很好实现,这里就带大家实现一下:
- 页面搭建
<?xml version="1.0" encoding="utf-8"?>
这里就不讲解ProgressBar实现水平进度条了,百度一大堆!!!
- 代码实现
private java.lang.String mUrl = "https://www.baidu.com/"; private WebView mWb; private ProgressBar progressBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mWb = findViewById(R.id.wb); progressBar = findViewById(R.id.pb_progress); progressBar.setMax(100); setWebView(mWb); } @SuppressLint("SetJavaScriptEnabled") private void setWebView(WebView wb) { WebSettings settings = wb.getSettings(); settings.setJavaScriptEnabled(true); settings.setPluginState(WebSettings.PluginState.ON); settings.setUseWideViewPort(true); settings.setLoadWithOverviewMode(true); settings.setSupportZoom(true); wb.loadUrl(mUrl); wb.setWebViewClient(new WebViewClient() { @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { view.loadUrl(String.valueOf(request.getUrl())); return true; } @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } }); wb.setWebChromeClient(new WebChromeClient() { @Override public void onProgressChanged(WebView view, int newProgress) { super.onProgressChanged(view, newProgress); progressBar.setProgress(newProgress); } }); }
主要代码就这么多就能,这里设置了一些其他的属性,上面都讲解了,这里就不去说了。。。
WebView加载错误显示缺省页面
private java.lang.String mUrl = "https://google.com/"; private WebView mWb; private ProgressBar progressBar; private java.lang.String mErrorUrl = "file:///android_asset/test.html"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mWb = findViewById(R.id.wb); progressBar = findViewById(R.id.pb_progress); progressBar.setMax(100); setWebView(mWb); } @SuppressLint("SetJavaScriptEnabled") private void setWebView(WebView wb) { WebSettings settings = wb.getSettings(); settings.setJavaScriptEnabled(true); settings.setPluginState(WebSettings.PluginState.ON); settings.setUseWideViewPort(true); settings.setLoadWithOverviewMode(true); settings.setSupportZoom(true); wb.loadUrl(mUrl); wb.setWebViewClient(new WebViewClient() { @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { view.loadUrl(String.valueOf(request.getUrl())); return true; } @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) { super.onReceivedHttpError(view, request, errorResponse); // 这个方法在6.0才出现 int statusCode = errorResponse.getStatusCode(); System.out.println("onReceivedHttpError code = " + statusCode); if (404 == statusCode || 500 == statusCode) { view.loadUrl("about:blank");// 避免出现默认的错误界面 view.loadUrl(mErrorUrl); } } @RequiresApi(api = Build.VERSION_CODES.M) @Override public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { super.onReceivedError(view, request, error); // 断网或者网络连接超时 if (error.getErrorCode() == ERROR_HOST_LOOKUP || error.getErrorCode() == ERROR_CONNECT || error.getErrorCode() == ERROR_TIMEOUT) { view.loadUrl("about:blank"); // 避免出现默认的错误界面 view.loadUrl(mErrorUrl); } } @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { super.onReceivedError(view, errorCode, description, failingUrl); // 断网或者网络连接超时 if (errorCode == ERROR_HOST_LOOKUP || errorCode == ERROR_CONNECT || errorCode == ERROR_TIMEOUT) { view.loadUrl("about:blank"); // 避免出现默认的错误界面 view.loadUrl(mErrorUrl); } } }); wb.setWebChromeClient(new WebChromeClient() { @Override public void onProgressChanged(WebView view, int newProgress) { super.onProgressChanged(view, newProgress); Log.e("done", "onProgressChanged: " + newProgress); progressBar.setProgress(newProgress); } @Override public void onReceivedTitle(WebView view, String title) { super.onReceivedTitle(view, title); // android 6.0 以下通过title获取 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { if (title.contains("404") || title.contains("500") || title.contains("Error")) { view.loadUrl("about:blank");// 避免出现默认的错误界面 view.loadUrl(mErrorUrl); } } } }); }
因为WebView引起了google的重视,所以版本之间存在很多差异,所以这里区分了很多版本的问题,上面都有相应的注解!其实就是在断网或者网络重连的时候添加了相应的错误页面,防止用户看见那个难看的断网页面。增加用户的体验!
WebView与JS交互的实现
这里先贴一段测试的js代码,下面会用到:
App调用js的代码 假装这个一个页面
1. 原生代码调用js中的代码
这里可以有两种实现方案:
这里注意一点,无论使用那种方案调用,前提是一定要有下面的代码!!!
WebSettings settings = mWb.getSettings(); settings.setJavaScriptEnabled(true);//允许相应js代码
通过loadUrl("javascript:callJS()")调用js中的代码,但是会刷新相应的页面(这里面callJS是上面function的方法名称)
通过evaluateJavascript(String script, ValueCallback
resultCallback) 的callBack进行调用相应的方法(这个处理效率高,但是这个方法是在API19才可以使用的,所以一般在使用的时候通过判断版本两个一起使用!!!)
整体的使用代码如下:
if (Build.VERSION.SDK_INT > 18) { mWb.evaluateJavascript("javascript:callJS()", new ValueCallback() { @Override public void onReceiveValue(String value) { Log.e("done", "onReceiveValue: " + value); } }); } else { mWb.loadUrl("javascript:callJS()"); }
这样既解决了效率的问题也解决了版本的问题。
2. 原生代码调用js中的代码
这里最常用的方式就是通过注释@JavascriptInterface进行调用,但是看了Carson_Ho的文章说这里有漏洞,但是说4.2就没有这种漏洞了,所以没有去研究,感觉现在一般的手机都应该超过4.2了,如果以后需要适配的时候在仔细看一下!!!
- 步骤1:
创建一个类用于给JS提供相应的方法;
class JSMothod { @JavascriptInterface public void hello(String msg) { System.out.println("JS调用了Android的hello方法"); } }
这里有一点需要注意的,就是@JavascriptInterface这个注解一定要加上,否则对于4.0一下的手机可能会存在问题!!!切记。。。
- 步骤2:
添加相应的调用方法
addJavascriptInterface(Object object, String name);
这里面的两个参数分别是上面写的那个类对象和相应js中的方法名;具体的代码是这样的。。。
mWb.addJavascriptInterface(new JSMothod(),"test");
这样就能实现js调用Android的代码了,其实很简单。
基本上上面的内容就囊括了Android在使用WebView中的一些常见的使用场景,可能还有些内容没有想到,如果有什么不明白的,可以在下方给我留言,还希望这些内容可以帮到你!!!
更多相关文章
- Android的一些冷知识
- Android中Context的总结及其用法
- Android类库打包方法探究
- Android中滑屏初探 - scrollTo 以及 scrollBy方法使用说明
- Android:异步处理之Handler、Looper、MessageQueue之间的恩怨(三)
- 【起航计划 013】2015 起航计划 Android(安卓)APIDemo的魔鬼步伐
- Android(安卓)之 Choreographer 详细分析
- 利用Android(安卓)Studio、MAT对Android进行内存泄漏检测
- 【Android】实现登录、注册、数据库操作(极复杂)