原生webview的引擎是webkit,Android 4.4后直接使用了Chrome,那么是否还有其他引擎呢?有的,比如腾讯的X5内核和crosswalk,crosswalk我不太清楚,有兴趣的可以用用看,反正我没兴趣,目前腾讯的X5内核在国内已经很热门了,这也不排除腾讯的宣传效果,官方公布的X5内核的优势如下:
1) 速度快:相比系统webview的网页打开速度有30+%的提升;
2) 省流量:使用云端优化技术使流量节省20+%;
3) 更安全:安全问题可以在24小时内修复;
4) 更稳定:经过亿级用户的使用考验,CRASH率低于0.15%;
5) 兼容好:无系统内核的碎片化问题,更少的兼容性问题;
6) 体验优:支持夜间模式、适屏排版、字体设置等浏览增强功能;
7) 功能全:在Html5、ES6上有更完整支持;
8) 更强大:集成强大的视频播放器,支持视频格式远多于系统webview;
9) 视频和文件格式的支持x5内核多于系统内核;
10) 防劫持是x5内核的一大亮点。
在这里我可以大胆的猜测,X5内核其实就是基于webkit上进一步优化,腾讯采用填坑的方式将webkit封装成了X5。
由于原生webview的坑比较多,不知道TX要填到什么时候才能将X5完善,一个完善的X5内核还是令人比较期待了,毕竟本人已经被原生webview坑了好久了。
WebView的状态,我把它理解为生命周期:
//激活WebView为活跃状态,能正常执行网页的响应
webView.onResume() ;
//当页面被失去焦点被切换到后台不可见状态,需要执行onPause
//通过onPause动作通知内核暂停所有的动作,比如DOM的解析、plugin的执行、JavaScript执行。
webView.onPause();
//当应用程序(存在webview)被切换到后台时,这个方法不仅仅针对当前的webview而是全局的全应用程序的webview
//它会暂停所有webview的layout,parsing,javascripttimer。降低CPU功耗。
webView.pauseTimers()
//恢复pauseTimers状态
webView.resumeTimers();
//销毁Webview
//在关闭了Activity时,如果Webview的音乐或视频,还在播放。就必须销毁Webview
//但是注意:webview调用destory时,webview仍绑定在Activity上
//这是由于自定义webview构建时传入了该Activity的context对象
//因此需要先从父容器中移除webview,然后再销毁webview:
rootLayout.removeView(webView);
webView.destroy();
当了解生命周期的前提下,我们不得不考虑webview内存泄漏的问题,网上也有相关的总结:
WebView内存泄漏--解决方法小结
从根源解决WebView内存泄漏、
前进和后退
//是否可以后退
Webview.canGoBack()
//后退网页
Webview.goBack()
//是否可以前进
Webview.canGoForward()
//前进网页
Webview.goForward()
//以当前的index为起始点前进或者后退到历史记录中指定的steps
//如果steps为负数则为后退,正数则为前进
Webview.goBackOrForward(intsteps)
Android客户端的返回键分为:硬件返回和软件返回,我们可以监听这两种返回,然后执行Webview.canGoBack()和Webview.goBack()。
实际上,如果第一次加载的网页有重定向链接时,可能会发生无法返回退出当前页面的效果,那么因为当返回到第一个历史网页时,就又被重定向了,我能想到的操作方案如下:
连续点击两次返回键,强制退出当前页面;(显然这样做不友好)
在导航栏增加一个关闭当前页面的按钮;(这是很多APP常用的方式)
-
捕获历史记录,对有重定向的链接做特定的处理
//获取历史 WebBackForwardList mWebBackForwardList = webvie.copyBackForwardList();
监听物理返回有两种方式:
mywebview.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (keyCode == KeyEvent.KEYCODE_BACK && mywebview.canGoBack()) { //表示按返回键
mywebview.goBack();
return true;
}
}
return false;
}
});
或者
@Override
public void onBackPressed() {
if(mywebview != null && mywebview.canGoBack()){
mywebview.goBack();
}else{
finish();
}
}
清除缓存数据
//清除网页访问留下的缓存
//由于内核缓存是全局的因此这个方法不仅仅针对webview而是针对整个应用程序
Webview.clearCache(true);
//清除当前webview访问的历史记录
//只会webview访问历史记录里的所有记录除了当前访问记录
Webview.clearHistory();
//这个api仅仅清除自动完成填充的表单数据,并不会清除WebView存储到本地的数据
Webview.clearFormData();
这里需要注意的是,如果你使用了cookie,按照需求决定是否需要清除cookie
下面是完整的onDestroy代码
@Override
protected void onDestroy() {
//清除Cookie
CookieSyncManager.createInstance(this);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.removeAllCookie();
CookieSyncManager.getInstance().sync();
if( mywebview!=null) {
mywebview.setWebChromeClient(null);
mywebview.setWebViewClient(null);
//清除网页访问留下的缓存,由于内核缓存是全局的因此这个方法不仅仅针对webview而是针对整个应用程序
mywebview.clearCache(true);
//只会webview访问历史记录里的所有记录除了当前访问记录
mywebview.clearHistory();
//这个api仅仅清除自动完成填充的表单数据,并不会清除WebView存储到本地的数据
mywebview.clearFormData();
// 如果先调用destroy()方法,则会命中if (isDestroyed()) return;这一行代码,需要先onDetachedFromWindow(),再
// destory()
ViewParent parent = mywebview.getParent();
if (parent != null) {
((ViewGroup) parent).removeView(mywebview);
}
mywebview.stopLoading();
// 退出时调用此方法,移除绑定的服务,否则某些特定系统会报错
mywebview.getSettings().setJavaScriptEnabled(false);
mywebview.clearView();
mywebview.removeAllViews();
mywebview.destroy();
}
super.onDestroy();
}
下面发一下Cookie的同步代码
public class SyncWebCookies {
private SyncWebCookies(){}
static class SingleHolder{
public static SyncWebCookies instance = new SyncWebCookies();
}
public static SyncWebCookies getInstance(){
return SingleHolder.instance;
}
/**
* 获取新cookie
* @param
*/
private static String getNewCookie(String cookie) {
if (cookie != null && cookie.contains(";")) {
cookie = cookie.substring(cookie.lastIndexOf(";")+1, cookie.length());
}
return cookie;
}
public static void synchronousWebCookies(Context mContext, String url) {
CookieManager cookieManager = CookieManager.getInstance();
String cookies = cookieManager.getCookie(url);
cookies = getNewCookie(cookies);//获取最终cookie
if (!TextUtils.isEmpty(cookies)) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
CookieSyncManager.createInstance(mContext);
}
cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
cookieManager.removeSessionCookie();// 移除
cookieManager.removeAllCookie();
StringBuilder sbCookie = new StringBuilder();
sbCookie.append(cookies);
String cookieValue = sbCookie.toString();
//为url设置cookie
cookieManager.setCookie(url, cookieValue);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//同步cookie
CookieManager.getInstance().flush();
}else{
//同步cookie
CookieSyncManager.getInstance().sync();
}
}
}
}
代码的逻辑是这样的,获取原有cookie,然后取cookie最后一个“;”的cookie,再同步。原因有以下几点:
(1)当有新的cookie生成时,新的cookie会添加到原有cookie之后,cookie和cookie之间用分号隔开,也就是说,最后一个cookie才是最新的cookie;
(2)cookieManager.setCookie(url, cookieValue)当设置cookie时,如果cookieValue有分号,只能截取到第一个分号之前的数据,也就是说,如果cookieValue=“1234;5678”,那么调用该方法之后cookie是“1234”,而不是“1234;5678”;
--以上设置cookie的结论是结合以前项目总结的,当前并没有经过系统的测试。--
然而,怎么设置cookie是根据需求而定的。
如果涉及到网络,需要注册网络权限
网页加载
1.loadData(String data, String mimeType, String encoding)
String data= "###标题###我是段落
";
mywebview.loadData(data, "text/html", "UTF8");
输出结果如下:
感觉奇怪的是,命名encoding指明的是“utf-8”,但是还是乱码了,查询了一些资料才发现,这里encoding其实是不生效的,这是原生的一个bug。
解决方案是改成如下代码:
String data= "###标题###我是段落
";
mywebview.loadData(data, "text/html;charset=UTF-8", null);
输出结果是:
2.loadDataWithBaseURL(String baseUrl, String data,String mimeType, String encoding, String failUrl)
String data= "###标题###我是段落
";
mywebview.loadDataWithBaseURL(null, data, "text/html", "UTF-8", null);
输出效果如下:
该方法的encoding是生效的,这个方法是官方比较推荐的方法。官方之所以推荐使用这个方法不仅仅是因为encoding,官方文档中有“同源策略”的概念,这个也许需要结合代码才能理解:
在网上随便找了两个同源图片:
http://img.daimg.com/uploads/allimg/110825/3-110R5133545427.jpg
http://img.daimg.com/uploads/allimg/120302/3-1203021T03E04.jpg
String data = "";
String baseUrl = "http://img.daimg.com";
mywebview.loadDataWithBaseURL(baseUrl, data, "text/html", "utf-8", null);
data里面的图片路劲是相对路劲,在加载数据时,指定baseUrl之后才可以正确在webview中加载图片,效果如下:
3.loadUrl(String url)、loadUrl(String url, Map
加载网络图片
//加载网络图片
mywebview.loadUrl("http://img.daimg.com/uploads/allimg/110825/3-110R5133545427.jpg");
加载一个网址
//加载一个网址
mywebview.loadUrl("https://www.baidu.com");
Map map = new HashMap<>();
map.put("key", "value");
mywebview.loadUrl(url, map);
加载本地一个文件(android_asset或android_res)
//该文件的格式是html,webview支持加载html格式的文件,在webview中已网页的形式展示
mywebview.loadUrl("file:///android_asset/htmldemo3.html");
//该文件的格式是ui,这个后缀是我随便命名的,webview不能识别ui格式的文件,当我将这个文件拷贝到assets文件夹中时,必须选择一种AS支持的一种类型
mywebview.loadUrl("file:///android_asset/htmldemo3.ui");
//该文件的格式是txt,webview可以识别txt格式的文件
mywebview.loadUrl("file:///android_asset/htmldemo3.txt");
//该文件的格式是mp3,webview可以识别mp3格式的文件可以查看下方截图:
mywebview.loadUrl("file:///android_asset/htmldemo3.mp3");
webview可以尝试加载任意资源文件,我加载了excel文件,发现不能显示内容,总之,文件的格式有很多,webview到底支持哪些格式的文件需要尝试后才知道,最后再扩展一下,文件url的协议不一定是file, 也有可能是content,支持ContentProvider的协议。
//加载某一资源库文件
mywebview.loadUrl("content://authorities/person/xxxx");
4.postUrl(String url, byte[] postData)
一般我们加载网页是这样写mywebview.loadUrl("http://www.xxx.xxx/demo?username='zhangsan'&age=12"),这是一个典型的get请求,但是如果需要post请求呢?这时就会需要postUrl(String url, byte[] postData)的支持。
具体用法如下:
public class User {
String username;
int age;
}
User user = new User();
user.username = "za";
user.age = 12;
//使用POST请求加载指定的网页
try {
mywebview.postUrl(url, concatParams(getAllFields(user)).getBytes());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NullPointerException e){
e.printStackTrace();
}
/**
*
* @param t
* @param
* @return
*/
private Map getAllFields(T t){
Field[] field = t.getClass().getDeclaredFields();
Map fieldsAndValues = new HashMap<>();
try {
for (int i = 0; i < field.length; i++) {
String name = field[i].getName();
if(field[i].getGenericType().toString().equals("class java.lang.String")){
String value = (String) field[i].get(t);
fieldsAndValues.put(name,value);
}
}
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
System.out.print(""+fieldsAndValues.size());
return fieldsAndValues;
}
private String concatParams(Map params) throws UnsupportedEncodingException {
if(params.size() ==0){
return null;
}
StringBuilder builder = new StringBuilder();
Set keys = params.keySet();
Iterator iterator = keys.iterator();
while (iterator.hasNext()){
String key = iterator.next();
String value = URLEncoder.encode(params.get(key), "UTF-8");
builder.append(String.format("%s=%s&",key, value));
}
builder.deleteCharAt(builder.lastIndexOf("&"));
return builder.toString();
}
5.重新加载当前网页reload()
mywebview.reload();
webview常用配置
webview一般需要支持JS,如果不支持,所有的JS功能将失效
//是否支持JS,如果访问的页面中要与Javascript交互,则webview必须设置支持Javascript
mywebview.getSettings().setJavaScriptEnabled(true);
窗口相关的配置一般用的比较少,了解就行
//让JavaScript自动打开窗口,默认false。适用于JavaScript方法window.open()。
mywebview.getSettings().setJavaScriptCanOpenWindowsAutomatically(false);
//设置WebView是否支持多窗口。如果设置为true,主程序要实现onCreateWindow(WebView, boolean, boolean, Message),默认false。
mywebview.getSettings().setSupportMultipleWindows(false);
//当WebView切换到后台但仍然与窗口关联时是否raster tiles,打开它可以避免在WebView从后台切换到前台时重新绘制,默认值false。在这种模式下后台的WebView占用更多的内存。请按如下准则显示内存的使用:
//WebView的尺寸不能比设备的屏幕尺寸更大;
//限制在少数WebView上使用该模式;
//在可见的WebView和即将显现的WebView上使用;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mywebview.getSettings().setOffscreenPreRaster(true);
}
缩放功能
mywebview.getSettings().setSupportZoom(true);//是否支持缩放功能(mywebview.getSettings().supportZoom()可以判断当前是否支持缩放)
mywebview.getSettings().setBuiltInZoomControls(true);//设置内置的缩放控件。若为false,则该WebView不可缩放
mywebview.getSettings().setDisplayZoomControls(false);//显示或隐藏原生的缩放控件
//mywebview.getSettings().setDefaultZoom();//已废弃
//mywebview.getSettings().setEnableSmoothTransition()//已废弃
需要注意的是:
(1)网页端可以将缩放功能屏蔽掉,如果加上以上代码依然不能缩放的话,则说明网页上已经将缩放功能屏蔽。
(2)当原生缩放控件渐变动画未结束前就销毁当前activity,会造成内存泄漏,再次打开activity时还有可能造成以下错误:
java.lang.IllegalArgumentException: Receiver not registered: android.widget.ZoomButtonsController$1@14ff16a
at android.app.LoadedApk.forgetReceiverDispatcher(LoadedApk.java:856)
at android.app.ContextImpl.unregisterReceiver(ContextImpl.java:1352)
at android.content.ContextWrapper.unregisterReceiver(ContextWrapper.java:576)
at android.widget.ZoomButtonsController.setVisible(ZoomButtonsController.java:404)
at android.widget.ZoomButtonsController$2.handleMessage(ZoomButtonsController.java:178)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:150)
at android.app.ActivityThread.main(ActivityThread.java:5546)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:794)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:684)
设置字体和字体大小
//API14版本以上已废弃。请取代使用setTextZoom(int)。设置页面文本的尺寸,默认NORMAL。
//mywebview.getSettings().setTextSize();
//设置页面上的文本缩放百分比,默认100
mywebview.getSettings().setTextZoom(100);
//设置WebView字体库字体,默认“cursive”
mywebview.getSettings().setCursiveFontFamily("cursive");
//设置fantasy字体集(font family)的名字默认为“fantasy”
mywebview.getSettings().setFantasyFontFamily("fantasy");
//设置固定的字体集的名字,默认为”monospace”。
mywebview.getSettings().setFixedFontFamily("monospace");
//设置默认固定的字体大小,默认为16,可取值1到72
mywebview.getSettings().setDefaultFixedFontSize(16);
//设置默认的字体大小,默认16,可取值1到72
mywebview.getSettings().setDefaultFontSize(16);
//设置最小的字号,默认为8
mywebview.getSettings().setMinimumFontSize(8);
//设置最小的本地字号,默认为8。
mywebview.getSettings().setMinimumLogicalFontSize(8);
//设置标准字体集的名字,默认值“sans-serif”。
mywebview.getSettings().setStandardFontFamily("sans-serif");
//设置衬线字体集(serif font family)的名字,默认“sans-serif”。
mywebview.getSettings().setSerifFontFamily("sans-serif");
//设置无衬线字体集(sans-serif font family)的名字。默认值”sans-serif”.
mywebview.getSettings().setSansSerifFontFamily("sans-serif");
这里设置字体和设置字体大小基本不用,默认就行,这里需要关心的是setTextZoom这个方法,默认值是100,作用有两个:
(1)可以设置webview的字体大小;
(2)当设置手机自带的字体大小时,webview的字体大小会随之变化,setTextZoom可以保证webview字体大小不随手机自带字体大小的变化而变化。
webview编码问题
//设置默认的字符编码集,默认”UTF-8”
mywebview.getSettings().setDefaultTextEncodingName("utf-8");//设置编码格式
webview通过JS进行文件访问
//是否允许在WebView中访问内容URL(Content Url),默认允许。内容Url访问允许WebView从安装在系统中的内容提供者载入内容。
mywebview.getSettings().setAllowContentAccess(false);
//设置是否允许 WebView 使用 File 协议,默认设置为true,即允许在 File 域下执行任意 JavaScript 代码
//使用 file 域加载的 js代码能够使用进行同源策略跨域访问,从而导致隐私信息泄露
//1.源策略跨域访问:对私有目录文件进行访问
//2.针对 IM 类产品,泄露的是聊天信息、联系人等等
//2.针对浏览器类软件,泄露的是cookie 信息泄露
mywebview.getSettings().setAllowFileAccess(false);
//设置是否允许通过 file url 加载的 Js代码读取其他的本地文件
//在Android 4.1前默认允许,在Android 4.1后默认禁止
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
mywebview.getSettings().setAllowFileAccessFromFileURLs(false);
// 设置是否允许通过 file url 加载的 Javascript 可以访问其他的源(包括http、https等源)
//在Android 4.1前默认允许、在Android 4.1后默认禁止
mywebview.getSettings().setAllowUniversalAccessFromFileURLs(false);
}
由于安全性的问题,如果以上方法设置成true,攻击者可以通过JS操作手机本地文件,包括app内核app外,这是一个非常严重的安全问题,在以后的配置中,直接把以上设置成false即可。
webview缓存
//应用缓存API是否可用,默认值false, 结合setAppCachePath(String)使用。
mywebview.getSettings().setAppCacheEnabled(true);
String storePath = Environment.getExternalStorageDirectory().getPath() + "/webcache/";
//storePath路径是外部路径,非APP路劲,所以需要配置文件存储和读取文件的权限
File file = new File(storePath);
file.mkdirs();
//设置应用缓存文件的路径。为了让应用缓存API可用,此方法必须传入一个应用可写的路径。该方法只会执行一次,重复调用会被忽略。
mywebview.getSettings().setAppCachePath(storePath);
//mywebview.getSettings().setAppCacheMaxSize(1000);//设置缓存最大值,现在已被弃用,缓存的管理变为自动管理
//重写使用缓存的方式,默认值LOAD_DEFAULT。缓存的使用方式基于导航类型,正常的页面加载,检测缓存,需要时缓存内容复现。导航返回时,内容不会复现,只有内容会从缓存盘中恢复。该方法允许客户端通过指定LOAD_DEFAULT, LOAD_CACHE_ELSE_NETWORK, LOAD_NO_CACHE or LOAD_CACHE_ONLY的其中一项来重写其行为。
//LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
//LOAD_DEFAULT: 根据cache-control决定是否从网络上取数据。
//LOAD_CACHE_NORMAL: API level 17中已经废弃, 从API level 11开始作用同LOAD_DEFAULT模式
//LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
//LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据
if(isNetworkConnected(MainActivity.this)){
mywebview.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);
}else{
mywebview.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
}
//DOM存储API是否可用,默认false。
mywebview.getSettings().setDomStorageEnabled(true);
//数据库存储API是否可用,默认值false。如何正确设置数据存储API参见setDatabasePath(String)。该设置对同一进程中的所有WebView实例均有效。注意,只能在当前进程的任意WebView加载页面之前修改此项,因为此节点之后WebView的实现类可能会忽略该项设置的改变。
mywebview.getSettings().setDatabaseEnabled(true);
//mywebview.getSettings().setDatabasePath();//已废弃
//已废弃,WebView是否保存表单数据,默认值true。
//mywebview.getSettings().setSaveFormData(true);
//API18以上版本已废弃。未来版本将不支持保存WebView中的密码。设置WebView是否保存密码,默认true。
//mywebview.getSettings().setSavePassword(true);
这里着重理解一下文本view的缓存模式,代码中给出的策略是,如果网络可用模式为LOAD_DEFAULT,否则模式为LOAD_CACHE_ELSE_NETWORK。
图片和数据的加载
//是否禁止从网络(通过http和https URI schemes访问的资源)下载图片资源,默认值为false。注意,除非getLoadsImagesAutomatically()返回true,否则该方法无效。还请注意,即使此项设置为false,使用setBlockNetworkLoads(boolean)禁止所有网络加载也会阻止网络图片的加载。当此项设置的值从true变为false,WebView当前显示的内容所引用的网络图片资源会自动获取。
mywebview.getSettings().setBlockNetworkImage(false);
//WebView是否下载图片资源,默认为true。注意,该方法控制所有图片的下载,包括使用URI嵌入的图片(使用setBlockNetworkImage(boolean) 只控制使用网络URI的图片的下载)。如果该设置项的值由false变为true,WebView展示的内容所引用的所有的图片资源将自动下载。
mywebview.getSettings().setLoadsImagesAutomatically(true);
//是否禁止从网络下载数据,如果app有INTERNET权限,默认值为false,否则默认为true。使用setBlockNetworkImage(boolean) 只会禁止图片资源的加载。注意此值由true变为false,当前WebView展示的内容所引用的网络资源不会自动加载,直到调用了重载。如果APP没有INTERNET权限,设置此值为false会抛出SecurityException。
mywebview.getSettings().setBlockNetworkLoads(false);
setBlockNetworkImage:允许或禁止网络图片加载(注意:重点是“网络”)
setLoadsImagesAutomatically:允许或禁止网络和本地图片的加载(注意:重点是“网络”和“本地”)
setBlockNetworkLoads:允许或禁止网络下载数据
webview定位
//定位是否可用,默认为true。请注意,为了确保定位API在WebView的页面中可用,必须遵守如下约定:
//(1) app必须有定位的权限,参见ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION;
//(2) app必须提供onGeolocationPermissionsShowPrompt(String, GeolocationPermissions.Callback)回调方法的实现,在页面通过JavaScript定位API请求定位时接收通知。
//作为可选项,可以在数据库中存储历史位置和Web初始权限,参见setGeolocationDatabasePath(String).
mywebview.getSettings().setGeolocationEnabled(true);
//mywebview.getSettings().setGeolocationDatabasePath();//已废弃
设置UserAgent
//设置WebView的用户代理字符串。如果字符串为null或者empty,将使用系统默认值。注意从KITKAT版本开始,加载网页时改变用户代理会让WebView再次初始化加载。
mywebview.getSettings().setUserAgentString(mywebview.getSettings().getUserAgentString());
如果是加载第三方网站,请不要随意更改别人的UserAgent,避免加载错误,因为你不清楚人家的加载逻辑中有没有涉及到对UserAgent的判断,UserAgent的一般用法是为了区别是哪一个客户端(比如:Android端可以设置为:android,IOS端可以设置为:ios,PC端可以设置为:pc)
允许使用轻触摸做出选择和光标悬停(已废弃)
//已废弃。从 JELLY_BEAN 开始,该设置无效。允许使用轻触摸做出选择和光标悬停。
//mywebview.getSettings().setLightTouchEnabled(false);
禁用菜单项模式行为包括menuItems标签(复制、粘贴、选择)
//禁用菜单项模式行为包括menuItems标签
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
mywebview.getSettings().setDisabledActionModeMenuItems(WebSettings.MENU_ITEM_NONE);
//mywebview.getSettings().setDisabledActionModeMenuItems(WebSettings.MENU_ITEM_PROCESS_TEXT);
//mywebview.getSettings().setDisabledActionModeMenuItems(WebSettings.MENU_ITEM_SHARE);
//mywebview.getSettings().setDisabledActionModeMenuItems(WebSettings.MENU_ITEM_WEB_SEARCH);
}
}catch (NoSuchMethodError noSuchMethodError){
}
这个就不写注释了,因为我在运行的时候崩溃了,报了NoSuchMethodError错误,总之如果以后有一个需求设计到这个再回来看吧。
webview适配
//设置布局,会引起WebView的重新布局(relayout),默认值NARROW_COLUMNS
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
mywebview.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING);
}else{
mywebview.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
}
//是否允许WebView度超出以概览的方式载入页面,默认false。即缩小内容以适应屏幕宽度。该项设置在内容宽度超出WebView控件的宽度时生效,例如当getUseWideViewPort() 返回true时。
mywebview.getSettings().setLoadWithOverviewMode(true);
//WebView是否支持HTML的“viewport”标签或者使用wide viewport。设置值为true时,布局的宽度总是与WebView控件上的设备无关像素(device-dependent pixels)宽度一致。当值为true且页面包含viewport标记,将使用标签指定的宽度。如果页面不包含标签或者标签没有提供宽度,那就使用wide viewport。
mywebview.getSettings().setUseWideViewPort(true);
setLayoutAlgorithm目前只剩下两种常量了:TEXT_AUTOSIZING和NORMAL,其他的都已经弃用了,目前不太清楚它们的界面效果。
但是将setLoadWithOverviewMode和setUseWideViewPort设置成true完全可以看出效果,没有适配的网页真的适配了。当然,网页本身也可以做到自适应。
播放视频相关
//WebView是否需要用户的手势进行媒体播放,默认值为true。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
mywebview.getSettings().setMediaPlaybackRequiresUserGesture(false);
}
默认情况下setMediaPlaybackRequiresUserGesture的值是true,需要手动点击视频才能播放,如果改成false,那么在网页滑动的时候就可以实现自动播放。
Android5.0上WebView中Http和Https混合问题
/**
* Android5.0上 WebView中Http和Https混合问题
* MIXED_CONTENT_ALWAYS_ALLOW:允许从任何来源加载内容,即使起源是不安全的;
* MIXED_CONTENT_NEVER_ALLOW:不允许Https加载Http的内容,即不允许从安全的起源去加载一个不安全的资源;(默认)
* MIXED_CONTENT_COMPATIBILITY_MODE:当涉及到混合式内容时,WebView 会尝试去兼容最新Web浏览器的风格。
**/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mywebview.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
在Android5.0上,为了提高安全性,https的页面不能直接访问http,导致一些功能不可用,以上代码可以解决这个问题。
请求焦点时是否需要设置节点获取焦点
//调用requestFocus(int, android.graphics.Rect)时是否需要设置节点获取焦点,默认值为true。
mywebview.getSettings().setNeedInitialFocus(true);
这个默认就是ture,不用管了。
插件相关
//mywebview.getSettings().setPluginsEnabled();//现在这个已经不再支持,被setPluginState替代
//在API18以上已废弃。未来将不支持插件,不要使用。告诉WebView启用、禁用或者有即用(on demand)的插件,即用模式是指如果存在一个可以处理嵌入内容的插件,会显示一个占位图标,点击时开启。默认值OFF。
//mywebview.getSettings().setPluginState(WebSettings.PluginState.ON);
以后的webview版本中都不会使用到插件,不用管,知道就行了。
调整线程优先级
//在API18以上已废弃。不建议调整线程优先级,未来版本不会支持这样做。设置绘制线程的优先级。不像其他设置,同一进程中只需调用一次,默认值NORMAL
//mywebview.getSettings().setRenderPriority(WebSettings.RenderPriority priority.);
官方都不建议调整优先级了,不用管。
webview安全浏览
//设置是否启用安全浏览。安全浏览功能允许WebView通过验证链接来防范恶意软件和网络钓鱼攻击。使用清单标签可以禁用所有WebView的安全浏览功能。清单标记的优先级低于此API。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mywebview.getSettings().setSafeBrowsingEnabled(true);
}
在Android8.0之后新增的特性,可以用代码控制安全浏览,其标签就是
由于文章太长,官方不让发布,所以文章就一分为二了
超详细的Webview攻略(二)