Android的Webview在低版本和高版本采用了不同的webkit版本内核,4.4后直接使用了Chrome。
加载HTML的四种方法
webView.loadUrl("http://139.196.35.30:8080/OkHttpTest/apppackage/test.html");//加载url
webView.loadUrl("file:///android_asset/test.html");//加载asset文件夹下html
//方式3:加载手机sdcard上的html页面
webView.loadUrl("content://com.ansen.webview/sdcard/test.html");
//方式4 使用webview显示html代码
webView.loadDataWithBaseURL(null,"<html><head><title> 欢迎您 </title></head>" +
"<body><h2>使用webview显示 html代码</h2></body></html>", "text/html" , "utf-8", null);
WebSettings
private void initWebSetting(){
WebSettings webSettings = mWeb .getSettings();
mWeb.requestFocusFromTouch();//支持获取手势焦点,输入用户名、密码或其他
webSettings.setJavaScriptEnabled(true);
webSettings.setRenderPriority(WebSettings.RenderPriority.HIGH);//提高渲染的优先级
//设置自适应屏幕,两者合用
webSettings.setUseWideViewPort(true);//将图片调整到适合webview的大小
webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小
webSettings.setSupportZoom(true); //支持缩放,默认为true。是下面那个的前提。
webSettings.setBuiltInZoomControls(true);//设置内置的缩放控件。
//若上面是false,则该WebView不可缩放,这个不管设置什么都不能缩放。
webSettings.setTextZoom(2);//设置文本的缩放倍数,默认为 100
webSettings.setDisplayZoomControls(false); //隐藏原生的缩放控件
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN); //支持内容重新布局,调用该方法会引起页面重绘。
webSettings.supportMultipleWindows(); //多窗口
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //关闭webview中缓存
webSettings.setAllowFileAccess(true);
webSettings.setNeedInitialFocus(true);//当webview调用requestFocus时为webview设置节点
webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口
webSettings.setLoadsImagesAutomatically(true);//支持自动加载图片
webSettings.setDefaultTextEncodingName("utf-8");//设置编码格式
webSettings.setStandardFontFamily(""); //设置 WebView 的字体,默认字体为 "sans-serif"
webSettings.setDefaultFontSize(20);//设置 WebView 字体的大小,默认大小为 16
webSettings.setMinimumFontSize(12);//设置 WebView 支持的最小字体大小,默认为 8
webSettings.setBlockNetworkImage(true);//禁止WebView从网络上加载图片。默认值false允许。
webSettings.setUserAgentString("Android");//设置WebView的UserAgent值。
}
关于缓存CacheMode
- LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
- LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取数据。
- LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
- LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
结合使用(离线加载):
if (NetStatusUtil.isConnected(getApplicationContext())) {
webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);//根据cache-control决定是否从网络上取数据。
} else {
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);//没网,则从本地获取,即离线加载
}
webSettings.setDomStorageEnabled(true); // 开启 DOM storage API 功能
webSettings.setDatabaseEnabled(true); //开启 database storage API 功能
webSettings.setAppCacheEnabled(true);//开启 Application Caches 功能
String cacheDirPath = getFilesDir().getAbsolutePath() + APP_CACAHE_DIRNAME;
webSettings.setAppCachePath(cacheDirPath); //设置 Application Caches 缓存目录
//每个 Application 只调用一次 WebSettings.setAppCachePath(),WebSettings.setAppCacheMaxSize()
添加 HTTP 请求头(Header)
loadUrl(String url, Map<String, String> additionalHttpHeaders)
WebViewClient
WebViewClient就是帮助WebView处理各种通知、请求事件的。
打开网页时不调用系统浏览器, 而是在本WebView中显示:
mWeb.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return false;
//True if the host application wants to leave the current webview and handle the url itself, otherwise return false.
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
return super.shouldOverrideUrlLoading(view, request);
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
//这个事件就是开始载入页面调用的,我们可以设定一个loading的页面,告诉用户程序在等待网络响应。
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
//在页面加载结束时调用。同样道理,我们可以关闭loading 条,切换程序动作。
}
@Override
public void onLoadResource(WebView view, String url) {
super.onLoadResource(view, url);
// 在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次。
}
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
super.onReceivedError(view, request, error);// (报告错误信息)
}
@Override
public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) {
super.doUpdateVisitedHistory(view, url, isReload); //(更新历史记录)
}
@Override
public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {
return super.shouldOverrideKeyEvent(view, event);
//重写此方法才能够处理在浏览器中的按键事件。
}
@Override
public void onFormResubmission(WebView view, Message dontResend, Message resend) {
super.onFormResubmission(view, dontResend, resend); //(应用程序重新请求网页数据)
}
@Override
public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
super.onReceivedHttpAuthRequest(view, handler, host, realm); //(获取返回信息授权请求)
}
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
super.onReceivedSslError(view, handler, error);//重写此方法可以让webview处理https请求。
}
@Override
public void onScaleChanged(WebView view, float oldScale, float newScale) {
super.onScaleChanged(view, oldScale, newScale); // (WebView发生改变时调用)
}
@Override
public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
super.onUnhandledKeyEvent(view, event);//(Key事件未被加载时调用)
}
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
return super.shouldInterceptRequest(view, request);
// 拦截替换网络请求数据, 从API 21开始引入
}
});
WebChromeClient
WebChromeClient是辅助WebView处理Javascript的对话框,网站图标,网站title,加载进度等
WebChromeClient Chromeclient = new WebChromeClient(){
@Override
public void onProgressChanged(WebView view, int newProgress) {
//newProgress 0-100整数
}
@Override
public void onReceivedTitle(WebView view, String title) {
MainActivity.this.setTitle(title);
}
@Override
public void onReceivedIcon(WebView view, Bitmap icon) {
//
}
@Override
public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
//
return true;
}
@Override
public void onCloseWindow(WebView window) {
}
//不支持js的alert弹窗,需要自己监听然后通过dialog弹窗
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
AlertDialog.Builder localBuilder = new AlertDialog.Builder(webView.getContext());
localBuilder.setMessage(message).setPositiveButton("确定",null);
localBuilder.setCancelable(false);
localBuilder.create().show();
//注意:
//必须要这一句代码:result.confirm()表示:
//处理结果为确定状态同时唤醒WebCore线程
//否则不能继续点击按钮
result.confirm();
return true;
}
//处理confirm弹出框
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult
result) {
//
return true;
}
//处理prompt弹出框
@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
//
return true;
}
JS和android 互调
对于Android调用JS代码的方法有2种:
- 通过WebView的loadUrl()
- 通过WebView的evaluateJavascript()
对于JS调用Android代码的方法有3种:
- 通过WebView的addJavascriptInterface()进行对象映射 被JS调用的方法必须加入@JavascriptInterface注解
- 通过 WebViewClient 的shouldOverrideUrlLoading ()方法回调拦截 url
- 通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt() 消息
参考交互方式
实现webview调用系统文件选择器
原来5.0的WebChromeClient不是用的openFileChooser(…),而是用的onShowFileChooser(…)
private ValueCallback<Uri[]> mUploadCallbackAboveFive;
private Activity mActivity;
private ValueCallback<Uri> mUploadMessage;
// For Android < 3.0
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
openFileChooser(mUploadMessage, "", "");
}
// For Android 3.0+
public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
openFileChooser(mUploadMessage, acceptType, "");
}
//For Android 4.1
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
mActivity.startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
}
/**
* 兼容5.0及以上
*
* @param webView
* @param valueCallback
* @param fileChooserParams
* @return
*/
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> valueCallback, android.webkit.WebChromeClient.FileChooserParams fileChooserParams) {
mUploadCallbackAboveFive = valueCallback;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
mActivity.startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
return true;
}
@Override
public void onActivityResultInternal(int resultCode, Intent data) {
if (getPhoneAndroidSDK() < Build.VERSION_CODES.LOLLIPOP) {//5.0之前拿到照片的方式不相同
if (fromTakePhoto && requestCode == 1 && resultCode == -1) {
mWeb.loadUrl("javascript:wave2('" + fileFullName + "')");
} else {
mWeb.loadUrl("javascript:wave2('Please take your photo')");
}
fromTakePhoto = false;
if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage)
return;
Uri result = data == null || resultCode != RESULT_OK ? null : data
.getData();
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
}
} else {
if (requestCode != FILECHOOSER_RESULTCODE
|| mUploadCallbackAboveL == null) {
return;
}
Uri[] results = null;
if (resultCode == Activity.RESULT_OK) {
if (data == null) {
} else {
String dataString = data.getDataString();
ClipData clipData = data.getClipData();
if (clipData != null) {
results = new Uri[clipData.getItemCount()];
for (int i = 0; i < clipData.getItemCount(); i++) {
ClipData.Item item = clipData.getItemAt(i);
results[i] = item.getUri();
}
}
if (dataString != null)
results = new Uri[]{Uri.parse(dataString)};
}
}
mUploadCallbackAboveL.onReceiveValue(results);
mUploadCallbackAboveL = null;
return;
}
}
在 WebView 中长按保存图片
首先设置监听
mWebview.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
}
});
获取类型
WebView.HitTestResult result = ((WebView) v).getHitTestResult();
int type = result.getType(); //图片类型
String imgurl = result.getExtra(); //图片地址
WebView.HitTestResult.UNKNOWN_TYPE 未知类型
WebView.HitTestResult.PHONE_TYPE 电话类型
WebView.HitTestResult.EMAIL_TYPE 电子邮件类型
WebView.HitTestResult.GEO_TYPE 地图类型
WebView.HitTestResult.SRC_ANCHOR_TYPE 超链接类型
WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE 带有链接的图片类型
WebView.HitTestResult.IMAGE_TYPE 单纯的图片类型
WebView.HitTestResult.EDIT_TEXT_TYPE 选中的文字类型
最后操作图片
mWebView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
WebView.HitTestResult result = ((WebView)v).getHitTestResult();
if (null == result)
return false;
int type = result.getType();
if (type == WebView.HitTestResult.UNKNOWN_TYPE)
return false;
// 这里可以拦截很多类型,我们只处理图片类型就可以了
switch (type) {
case WebView.HitTestResult.PHONE_TYPE: // 处理拨号
break;
case WebView.HitTestResult.EMAIL_TYPE: // 处理Email
break;
case WebView.HitTestResult.GEO_TYPE: // 地图类型
break;
case WebView.HitTestResult.SRC_ANCHOR_TYPE: // 超链接
break;
case WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE:
break;
case WebView.HitTestResult.IMAGE_TYPE: // 处理长按图片的菜单项
// 获取图片的路径
String saveImgUrl = result.getExtra();
// 跳转到图片详情页,显示图片
Intent i = new Intent(MainActivity.this, ImageActivity.class);
i.putExtra("imgUrl", saveImgUrl);
startActivity(i);
break;
default:
break;
}
}
});
Android5.0 WebView中Http和Https混合问题
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
参数类型说明:
MIXED_CONTENT_ALWAYS_ALLOW:允许从任何来源加载内容,即使起源是不安全的;
MIXED_CONTENT_NEVER_ALLOW:不允许Https加载Http的内容,即不允许从安全的起源去加载一个不安全的资源;
MIXED_CONTENT_COMPATIBILITY_MODE:当涉及到混合式内容时,WebView 会尝试去兼容最新Web浏览器的风格。
在5.0以下 Android 默认是 全允许,
但是到了5.0以上,就是不允许,实际情况下很我们很难确定所有的网页都是https的,所以就需要这一步的操作。
避免WebView内存泄露的方式
1不再xml中定义WebView,而是用代码动态添加
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
mWebView = new WebView(getApplicationContext());
mWebView.setLayoutParams(params);
mLayout.addView(mWebView);
2.在 Activity 销毁的时候,可以先让 WebView 加载null内容,然后移除 WebView,再销毁 WebView,最后置空。
@Override
protected void onDestroy() {
if (mWebView != null) {
mWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
mWebView.clearHistory();
((ViewGroup) mWebView.getParent()).removeView(mWebView);
mWebView.destroy();
mWebView = null;
}
super.onDestroy();
}
WebView的方法
goBack()//后退
goForward()//前进
goBackOrForward(intsteps) //以当前的index为起始点前进或者后退到历史记录中指定的steps,
如果steps为负数则为后退,正数则为前进
canGoForward()//是否可以前进
canGoBack() //是否可以后退
清除数据:
clearCache(true);//清除网页访问留下的缓存,由于内核缓存是全局的因此这个方法不仅仅针对webview而是针对整个应用程序.
clearHistory()//清除当前webview访问的历史记录,只会webview访问历史记录里的所有记录除了当前访问记录.
clearFormData()//这个api仅仅清除自动完成填充的表单数据,并不会清除WebView存储到本地的数据。
WebView状态:
onResume() //激活WebView为活跃状态,能正常执行网页的响应
onPause()//当页面被失去焦点被切换到后台不可见状态,需要执行onPause动过, onPause动作通知内核暂停所有的动作,比如DOM的解析、plugin的执行、JavaScript执行。
pauseTimers()//当应用程序被切换到后台我们使用了webview, 这个方法不仅仅针对当前的webview而是全局的全应用程序的webview,它会暂停所有webview的layout,parsing,javascripttimer。降低CPU功耗。
resumeTimers()//恢复pauseTimers时的动作。
destroy()//销毁,关闭了Activity时,音乐或视频,还在播放。就必须销毁。
判断WebView是否已经滚动到页面底端 或者 顶端:
因为webview可以缩放
if (webView.getContentHeight() * webView.getScale() == (webView.getHeight() + webView.getScrollY())) {
//已经处于底端
}
if(webView.getScrollY() == 0){
//处于顶端
}
考拉团队的如何设计健壮的webview
漏洞
阿里巴巴的开发手册中强制要求
// 禁用 file 协议;
setAllowFileAccess(false);
setAllowFileAccessFromFileURLs(false);
setAllowUniversalAccessFromFileURLs(false);
会造成隐私的泄漏
你不知道的 Android WebView 使用漏洞
网友评论