Android webview详解
来源:互联网 发布:图片制作视频软件 手机 编辑:程序博客网 时间:2024/05/29 16:47
市面上的App大致分为3类::Native App、Web App和Hybrid App,Hybrid App兼具"Native App"良好的用户交互体验和"Web App"跨平台开发的优势
Webview解析
解决webview加载劣势——资源预加载:
资源加载缓慢: H5页面是从服务器上下发的,客户端的页面在内存里面,加载网页的时间更长,而且受限于网络情况,但是这种问题在某种程度上市可以弥补的,比如我们可以做一些资源预加载方案,下面列举资源预加载:
一、使用webview自身的缓存机制: 如果我们在App里面访问一个页面,短时间内再次访问这个页面,就会感觉第二次打开的时候顺畅很多,加载速度比第一次的时间要短,这个就是因为webview自身内部会做一些缓存,只要打开过得资源,他都会试着缓存到本地,第二次需要访问的时候它直接从本地读取,但这个读取其实是不太稳定的东西,关掉之后,或者说这种缓存失效之后,系统会自动把他清楚。我们在应用启动的时候开一个像素的webview,事先访问以下我们常用的资源,后续打开页面的时候如果再用到这些资源他就可以从本地读取,页面加载的时间会短一些。
二、自己去构建,自己管理缓存: 把这些需要预加载的资源放在App里面,他可能是预先放进去的,也可能是后续下载的,问题在于前端这些页面怎么去缓存,两个方案,第一种是前端可以在H5打包的时候把里面的资源URL进行替换,这样可以直接访问本地地址;第二种是客户端可以拦截这些网页发出的所有请求做替换:美团就是使用的的第二种预加载方案: 详情看美团大众点评Hybrid化建设,实现原理:每当webview发起资源请求的时候,我们会拦截这些资源的请求,去本地检查一下我们这些静态资源本地离线包有没有。针对本地的缓存文件我们有些策略能够及时的去更新它,为了安全考虑,也需要同时做一些预加载和安全包的加密工作。预加载有以下几点优势:
1、我们拦截了webview里面发出的所有的请求,但是并没有替换里面的前端应用的任何代码,前端这套页面代码可以在App内,或者其他的App里面都可以直接访问,他不需要为我们App做定制化的东西
2、这些URL请求,他会直接带上先前用户操作留下的Cookie,因为我们没有更改资源原始URL地址;
3、整个前端在用离线包和缓存文件的时候是完全无感知的,前端只用管写一个自己的页面,客户端会帮他处理好这样一些静态资源预加载的问题,有这个离线包的话加载速度会变快很多,特别是弱网情况下,没有这些离线包加载速度会慢一些。而且如果本地离线包的版本不能跟H5匹配的话,H5页面也不会发生什么问题。
WebView的常见设置:
WebSetting webSettings=webView.getSettings();
//设置这个属性为true允许webview和js代码进行交互,这个本身会有漏洞
webSettings.setJavaScriptEnabled(true);
//设置WebView是否可以打开WebView新窗口
webSettings.setJavaScriptCanOpenWindowAutomatically(true);
//webview是否支持多窗口,如果设置未true,需要重写
//WebChromeClient#onCreateWindow(WebView,boolean,boolean,Message)函数,默认为false
webSettings.setSuppportMutipleWindows(true);
//这个属性用来设置webview是否能够加载图片资源,包括哪些使用data uri协议嵌入的图片。使用setBlockNetworkImage(boolean)方法来控制仅仅加载使用网络URI协议的图片,需要提到的一点是如果这个设置从false变为true之后,所有被内容引用的正在显示的webview图片资源都会被自动加载,该标识默认值为true。
webSettings.setLoadsImagesAutomatically(false);
//标识是否加载网络上的图片(使用http或者https域名的资源),需要注意的是如果getLoadsImageAutomatically不返回true,这个标识将没有作用
webSettings.setBlockNetworkImage(boolean)
//显示webView提供的缩放控件
webSettings.setDisplayZoomControls(true);
webSettings.setBuiltInZoomControls(true)
//设置是否启动WebView API,默认值为false
webSettings.setDatabaseEnabled(true);
//打开webview的storage功能,这样JS的localStorage,sessionStorage对象才可以使用(比如一张网页的拼图js页面)
webSettings.setDomStorageEnabled(true);
//打开WebView的LBS功能,这样JS的geolocation对象才可以使用
webSettings.setGeolocationEnabled(true);
webSettings.setGeolocationDatabasePath("");
//设置是否打开webview表单数据的保存功能
webSettings.setSaveFormData(true);
//设置webview的默认的userAgent字符串
webSettings.setUserAgentString("");
//设置是否WebView支持"viewport"的HTML meta tag,这个标识用来屏幕自适应的,当这个标识设置为false时,页面布局的宽度被一直设置为css中控制的webview的宽度;如果设置为true并且页面含有viewport meta tag,那么被这个tag声明的宽度将会被使用。
//webSettings.setUseWideViewPort(false);
//设置webview的字体,可以通过这个函数,改变webview的字体,默认字体为"sans-serif"
webSettings.setStandardFontFamily("");
//设置webview字体的大小,默认大小为16
webSettings.setDefaultFontSize(20);
//设置webview支持的最小字体大小,默认为8
webSettings.setMinimumFontSize(12);
//设置页面是否支持缩放
webSettings.setSupportZoom(true);
//设置文本的缩放倍数,默认为100
webSettings.setTextZoom(2);
然后还有最常用的 WebViewClient 和 WebChromeClient,WebViewClient主要辅助WebView执行处理各种响应请求事件的,比如:
onLoadResource
onPageStart
onPageFinish
onReceiveError
onReceivedHttpAuthRequest
shouldOverrideUrlLoading
WebChromeClient 主要辅助 WebView 处理J avaScript 的对话框、网站 Logo、网站 title、load 进度等处理:
onCloseWindow(关闭WebView)
onCreateWindow
onJsAlert
onJsPrompt
onJsConfirm
onProgressChanged
onReceivedIcon
onReceivedTitle
onShowCustomView
public class JSObject { private Context mContext; public JSObject(Context context) { mContext = context; } @JavascriptInterface public String showToast(String text) { Toast.show(mContext, text, Toast.LENGTH_SHORT).show(); return "success"; }}...//特定版本下会存在漏洞mWebView.addJavascriptInterface(new JSObject(this), "myObj");
function showToast(){ var result = myObj.showToast("我是来自web的Toast");}第二种方式: 利用WebViewClient接口回调方法拦截url: 这种方式使用频次也很高,上面介绍的WebViewClient,其中有个回调接口shouldOverrideUrlLoading(WebView view,String url),我们利用这个拦截url,然后解析这个url的协议,如果发现是我们预先约定好的协议就开始解析参数,执行相应的逻辑,注意:这个方法在API24版本已经废弃了,需要使用shouleOverrideUrlLoading(WebView view,WebResourceRequest request)
public boolean shouldOverrideUrlLoading(WebView view, String url) { //假定传入进来的 url = "js://openActivity?arg1=111&arg2=222",代表需要打开本地页面,并且带入相应的参数 Uri uri = Uri.parse(url); String scheme = uri.getScheme(); //如果 scheme 为 js,代表为预先约定的 js 协议 if (scheme.equals("js")) { //如果 authority 为 openActivity,代表 web 需要打开一个本地的页面 if (uri.getAuthority().equals("openActivity")) { //解析 web 页面带过来的相关参数 HashMap<String, String> params = new HashMap<>(); Set<String> collection = uri.getQueryParameterNames(); for (String name : collection) { params.put(name, uri.getQueryParameter(name)); } Intent intent = new Intent(getContext(), MainActivity.class); intent.putExtra("params", params); getContext().startActivity(intent); } //代表应用内部处理完成 return true; } return super.shouldOverrideUrlLoading(view, url);}js中的代码指定location地址:
function openActivity(){ document.location = "js://openActivity?arg1=111&arg2=222";}webview通过shouldOverrideUrlLoading拦截js相关数据并做处理,处理完成后如果web端想要得到方法的返回值,只能通过webview的loadUrl方法去执行js方法把返回值传递回去,相关代码如下:
//javamWebView.loadUrl("javascript:returnResult(" + result + ")");//javascriptfunction returnResult(result){ alert("result is" + result);}备注:这种方式打开Native页面还是很合适的,制定好相应的协议,就能够让web端具有打开所有本地页面的能力了。
@Overridepublic boolean onJsAlert(WebView view, String url, String message, JsResult result) { return super.onJsAlert(view, url, message, result);}@Overridepublic boolean onJsConfirm(WebView view, String url, String message, JsResult result) { return super.onJsConfirm(view, url, message, result);}@Overridepublic boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { //假定传入进来的 message = "js://openActivity?arg1=111&arg2=222",代表需要打开本地页面,并且带入相应的参数 Uri uri = Uri.parse(message); String scheme = uri.getScheme(); if (scheme.equals("js")) { if (uri.getAuthority().equals("openActivity")) { HashMap<String, String> params = new HashMap<>(); Set<String> collection = uri.getQueryParameterNames(); for (String name : collection) { params.put(name, uri.getQueryParameter(name)); } Intent intent = new Intent(getContext(), MainActivity.class); intent.putExtra("params", params); getContext().startActivity(intent); //代表应用内部处理完成 result.confirm("success"); } return true; } return super.onJsPrompt(view, url, message, defaultValue, result);}通过WebChromeClient接口,拦截JS中的几个提示方法,也就是几种样式的对话框,在JS中有三个常用的对话框方法:
function clickprompt(){ var result=prompt("js://openActivity?arg1=111&arg2=222"); alert("open activity " + result);}
//javamWebView.loadUrl("javascript:show(" + result + ")");//javascript<script type="text/javascript">function show(result){ alert("result"=result); return "success";}</script>注意,调用的名字一定要对应上,要不然调用不成功,而且js的调用一定要在onPageFinished函数回调之后才能调用,要不然会失败。
final int version = Build.VERSION.SDK_INT;if (version < 18) { mWebView.loadUrl(jsStr);} else { /*jsStr是javascript执行脚本,ValueCallBack是执行完脚本的返回值,也可能是null,在没有返回的情况*/ mWebView.evaluateJavascript(jsStr, new ValueCallback<String>() { @Override public void onReceiveValue(String value) { //此处为 js 返回的结果 } });}参考自微信公众号——App架构师,Android WebView详解
- Android WebView(一) WebView详解
- Android中WebView详解
- Android WebView 详解
- Android WebView应用详解
- android之WebView详解
- Android中WebView详解
- Android WebView详解
- Android中WebView详解
- Android WebView详解
- Android webview使用详解
- Android webview使用详解
- android WebView全面详解
- Android:WebView全面详解
- Android webview使用详解
- Android webview使用详解
- Android webview使用详解
- Android WebView详解
- android webview 操作详解。
- 理解Python中的装饰器
- <!DOCTYPE>声明
- jvm内存组成+GC
- 【知识库】--mysql 表字段修改操作(193)
- ubuntu 复制命令
- Android webview详解
- android带动画的圆形进度条 各种差值器的含义
- $("div[XXX]"什么意思?
- win7配置虚拟机的IP
- 医院如何建立客户服务中心,有什么作用?
- QSqlDatabase: QMYSQL driver not loaded问题解决方法
- 关于web服务器、应用服务器、http服务器区别
- @Temporal的使用
- Android之低功耗蓝牙的基本使用