一、WebView基本用法
mWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = mWebView.getSettings();
webSettings.setSavePassword(false);
webSettings.setSaveFormData(false);
webSettings.setJavaScriptEnabled(true);
webSettings.setSupportZoom(false);
mWebView.setWebChromeClient(new MyWebChromeClient());
mWebView.addJavascriptInterface(new DemoJavaScriptInterface(), "demo");
mWebView.loadUrl("file:///android_asset/demo.html");
复制代码
1. 支持Js
mWebSettings.setJavaScriptCanOpenWindowsAutomatically(true);
mWebSettings.setJavaScriptEnabled(true);// 支持js
复制代码
2. 缩放设置
// 设置是否构建缩放工具,如果该属性设置为false,则不论其他属性如何设置,均不支持缩放。
mWebSettings.setBuiltInZoomControls(true);
mWebSettings.setSupportZoom(true);
// 不显示缩放控件
mWebSettings.setDisplayZoomControls(false);
复制代码
3. WebView适应屏幕大小
方法1:(推荐使用)
mWebSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小
mWebSettings.setUseWideViewPort(true); //将图片调整到适合webview的大小
复制代码
方法2:(deprecated)
mWebSettings.setLayoutAlgorithm(LayoutAlgorithm.SINGLE_COLUMN);
复制代码
LayoutAlgorithm是一个枚举用来控制页面的布局,有四个类型:
- NARROW_COLUMNS:可能的话使所有列的宽度不超过屏幕宽度(deprecated)
- NORMAL:正常显示不做任何渲染
- SINGLE_COLUMN:把所有内容放大webview等宽的一列中(deprecated)
- TEXT_AUTOSIZING:
方法3:主要针对平板设备
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int mDensity = metrics.densityDpi;
if (mDensity == 120) { //mdpi
settings.setDefaultZoom(ZoomDensity.CLOSE);
}else if (mDensity == 160) { //hdpi
settings.setDefaultZoom(ZoomDensity.MEDIUM);
}else if (mDensity == 240) { //xhdpi
settings.setDefaultZoom(ZoomDensity.FAR);
}
复制代码
4. WebViewClient
WebViewClient主要帮助处理各种通知请求事件等,比如页面开始加载,加载完成等。 用法:
webview.setWebViewClient(new MyWebViewClient());
复制代码
在此只介绍一些通用的方法,其他方法具体参见WebViewCLient源代码
public class MyWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);//WebView自己打开新的页面,不调用系统浏览器
return true;
//return super.shouldOverrideUrlLoading(view, url);
}
//页面加载完毕之后调用,在此可以做一些js注入
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
}
//页面开始加载时回调
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
}
//当接收到错误时回调,通常是网络不通
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
view.loadUrl("file:///android_asset/network_warning.htm");
}
//服务器返回错误时回调,例如返回400
@Override
public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
view.loadUrl("file:///android_asset/service_error.htm");
}
}
复制代码
5. WebChromeClient
WebChromeClient主要辅助WebView处理javascript对话框,网站图标,网站标题,加载进度等等。
webView.setWebChromeClient(new MyWebChromeClient());
复制代码
public class MyWebChromeClient extends WebChromeClient {
//接收到网站标题时回调,在此可以设置title
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
}
@Override
public void onReceivedIcon(WebView view, Bitmap icon) {
super.onReceivedIcon(view, icon);
}
@Override
public void onProgressChanged(WebView view, int newProgress) {
progressbar.setProgress(newProgress);
}
}
复制代码
6. 缓存机制
WebView提供了两种缓存:
- 数据缓存:分为AppCache和DOM Storage(Web Storage)两种;
- 网页缓存。是指加载一个网页时html、JS、CSS等页面或者资源数据。
缓存路径
WebView将缓存保存在内部存储的缓存目录中:/data/data/packageName/app_webview/。
WebView缓存路径: !
/shared_prefs/WebViewChromiumPrefs.xml里面保存了一些WebView的偏好设置 /app_webview中则保存了网页缓存。
WebView网页缓存文件 !
其中,在cache目录下保存了网页数据(html,css,js,图片,文本等),在访问缓存时,WebView会根据自己的缓存策略来查找缓存文件;其他文件保存了网页缓存。
WebView除了提供网页缓存之外,还有数据缓存,数据缓存由网页开发者控制,通常用来保存一些用户行为(例如:用户搜索记录)。我们只需要指定缓存的位置即可。
webview.getSettings().setAppCachePath(cachepath);
webview.getSettings().setAppCacheEnabled(true);
复制代码
注意:API版本不同,可能得到不同的结论,例如缓存路径可能为/data/data/packageName/cache/webviewCache,可能使用SQLite来保存缓存文件的索引等等,但唯一不变的是:WebView所有的缓存文件肯定都位于内部存储中,也即位于: /data/data/packageName/
但是我们在关于android存储方案你所需要知道的一切一文中提到过:内部存储的缓存目录不应该超过一定的限制,比如1M,而且当系统内部存储紧张时,会在不通知用户的前提下,将缓存文件删除。所以我们应该尽量将WebView的缓存保存在外部存储的缓存文件中。
7. 用户代理User Agent
在Web开发中,user-agent用来标识浏览器的类型,一般由浏览器自己填写,例如Win10-Chrome浏览器是:
Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36
复制代码
因此在移动开发中,我们可以使用user-agent来标识不同的设备,从而做出适配或统计分析,例如在华为荣耀7中UC浏览器的user-agent是:
Mozilla/5.0 (Linux; U; Android 6.0; zh-CN; PLK-UL00 Build/HONORPLK-UL00) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 UCBrowser/11.0.5.841 U3/0.8.0 Mobile Safari/534.30
复制代码
但是user-agent如此之长,不方便识别,因此我们通常会将user-agent设置为易分辨的字段,例如:"android"来表示该请求来自android手机
二、与Js交互
使用WebView可以调用html中的js代码,js也可以调用Java代码。两者要进行交互,那么就得有交互的接口。android中使用webView.addJavaScriptInterface(Object, String)方法来建立java与js之间的接口。
1. js调用Java代码
首先我们得定义js与java交互的接口,该接口只需要是一个Object对象即可
public class JavaScriptInterface{
@JavascriptInterface
public void callByJs(){
Log.i(TAG, "this is called by js");
webview.reload();
}
}
//建立js与java的关系
webview.addJavaScriptInterface(new JavaScriptInterface(), "reload");//“reload”参数在js中用到,用以标识JavaScriptInterface对象
复制代码
然后在js中调用该目标方法即可,调用方式如下:
<html>
<body>
<!-- Calls into the javascript interface for the activity -->
<a onClick="window.reload.callByJs()">
<div style="width:80px;
margin:0px auto;
padding:10px;
text-align:center;
border:2px solid #202020;" >
<img id="droid" src="android_normal.png"/><br>
Click me!
</div>
</a>
</body>
</html>
复制代码
使用方法十分简单,但要注意在api及其17以上,只有将方法添加**@JavascriptInterface**注解才可以被js调用。
Java调用js代码
在java中调用js代码也很easy。在上一节,我们点击超链接标签之后会打印出一个log,我们将它改为点击之后更换img中的src,因此我们只需要将JavaScriptInterface中的callByJs修改如下:
public class JavaScriptInterface{
@JavascriptInterface
public void callByJs(){
webView.loadUrl("javascript:callByJava()");
}
}
复制代码
然后将html文档修改为:
<html>
<script language="javascript">
/* This function is invoked by the activity */
function callByJava() {
document.getElementById("droid").src="android_waving.png";
}
</script>
<body>
<!-- Calls into the javascript interface for the activity -->
<a onClick="window.reload.callByJs()">
<div style="width:80px;
margin:0px auto;
padding:10px;
text-align:center;
border:2px solid #202020;" >
<img id="droid" src="android_normal.png"/><br>
Click me!
</div>
</a>
</body>
</html>
复制代码
再点击超链接标签之后就会将img的图片更换为android_waving.png。
注意:通过[官方文档](https://developer.android.com/reference/android/webkit/WebView.html#addJavascriptInterface(java.lang.Object, java.lang.String)可知,当TargetApi大于等于17时,js仅能调用添加了JavaScriptInterface注解的public methods,当targetApi小于17时,即便没有JavaScriptInterface注解,所有的public method也可以被js调用(包括继承而来的public method)。因此当TargetApi小于17时,js就可以使用反射来访问注入到js中的public域,所以当访问某些不信任的网页时,宿主App就可能被攻击。 还有,一定要记住,即便TargetApi >= 17,但如果你的App运行在android17以下,那么App还是有可能会被攻击。 所以,使用js注入最安全的方式就是仅允许TargetApi>=17且android4.2及以后才允许js访问Java对象。
三、混淆
在注入js之后,如果混淆了注入js的类,则注入的js就无法正确运行了,所以必须防止混淆。
不熟悉代码混淆的伙伴可以去看看这儿
-keepclassmembers class com.codemperor.summary.TestActivity$JavascriptMethodClass { public *; }
# 以下两行为在Android4.2版本以上的手机必须加入的对注解进行处理的配置
-keepattributes *Annotation*
-keepattributes *JavascriptInterface*
复制代码