WebView是一个用来展示web页面的控件。
1.WebSetting
WebView mWebView = (WebView)findViewById(R.id.webview); WebSettings webSettings = mWebView.getSettings(); //支持获取手势焦点,输入用户名、密码或其他 mWebView.requestFocusFromTouch(); //支持JS webSettings.setJavaScriptEnabled(true); //支持插件,没有该方法,,? //webSettings.setPluginsEnabled(true); //提高渲染的优先级 webSettings.setRenderPriority(WebSettings.RenderPriority.HIGH); //设置自适应屏幕,两者合用 webSettings.setUseWideViewPort(true);//将图片调整到合适webview的大小 webSettings.setLoadWithOverviewMode(true);//缩放至屏幕的大小 //支持缩放,默认为true,是下面那个的前提 webSettings.setSupportZoom(true); //设置内置的缩放控件,若上面是false,则该webview不可缩放,这个不管设置什么都不能缩放 webSettings.setBuiltInZoomControls(true); //隐藏原生的缩放控件 webSettings.setDisplayZoomControls(false); //支持内容重新布局 webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN); //多窗口 webSettings.supportMultipleWindows(); //关闭webView中缓存 webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //设置可以访问文件 webSettings.setAllowFileAccess(true); //当webView调用rewuestFocus时为webView设置节点 webSettings.setNeedInitialFocus(true); //支持通过JS打开新窗口 webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持自动加载图片 webSettings.setLoadsImagesAutomatically(true); //设置编码格式 webSettings.setDefaultTextEncodingName("utf-8");
缓存模式:
- LOAD_CACHE_ONLY:不适用网络,只读取本地缓存数据;
- LOAD_DEFAULT:(默认)根据cache-control决定是否从网络上取数据;
- LOAD_NO_CACHE:不适用缓存数据,只从网络获取数据;
- LOAD_CACHE_ELSE_NETWORK:只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。 结合使用(理线加载):
if (有网){ //根据cache-control决定是否从网络上获取数据 webSettings.setCacheMode(WebSettings.LOAD_DEFAULT); }else { //没网,则从本地获取,即离线加载 webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); } //开启DOM storage API功能 webSettings.setDomStorageEnabled(true); //开启database storage API功能 webSettings.setDatabaseEnabled(true); //开启Application Cache功能 webSettings.setAppCacheEnabled(true); String cacheDirPath = getFilesDir().getAbsolutePath()+"应用缓存路径名APP_CACAHE_DIRNAME"; //设置Application cache缓存目录 webSettings.setAppCachePath(cacheDirPath);
注意:每个Application只调用一次WebSettings.setAppCachePath(),WebSetting.setAppCacheMaxSize()。
2.加载方式
(1)加载一个网页:
mWebView.loadUrl("http://www.google.com/");
(2)加载apk包中的一个html页面:
mWebView.loadUrl("file:///android_asset/test.html");
(3)加载手机本地的一个html页面的方法:
mWebView.loadUrl("content://com.android.htmlfileprovider/sdcard/test.html");
3.WebViewClient
WebViewClient就是帮助WebView处理各种通知、请求事件的。
打开网页时,不调用系统浏览器,而是在本WebView中显示:mWebView.setWebViewClient(new WebViewClient(){ @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } });
WebViewClient的方法:
WebViewClient mWebViewClient = new WebViewClient(){ //最常用的,比如上面的。在网页上的所有加载都经过这个方法,这个函数我们可以做很多操作 //比如获取url,查看url.contains("add"),进行添加操作 @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { return super.shouldOverrideUrlLoading(view, url); } //重写此方法才能够处理在浏览器中的按键事件 @Override public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) { return super.shouldOverrideKeyEvent(view, event); } //这个时间就是开始载入页面调用的,我们可以设定一个loading的页面,告诉用户程序在等待网络响应 @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); } //在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次 @Override public void onLoadResource(WebView view, String url) { super.onLoadResource(view, url); } //拦截替换网络请求数据,API 11开始引入,API 21弃用 @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { return super.shouldInterceptRequest(view, url); } //拦截替换网络请求数据,API 21开始引入 @Override public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { return super.shouldInterceptRequest(view, request); } //报告错误信息 @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { super.onReceivedError(view, errorCode, description, failingUrl); } //更新历史记录 @Override public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) { super.doUpdateVisitedHistory(view, url, isReload); } //应用程序重新请求网页数据 @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); } //重写此方法可以让webview处理https请求 @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { super.onReceivedSslError(view, handler, error); } //webview发生改变时调用 @Override public void onScaleChanged(WebView view, float oldScale, float newScale) { super.onScaleChanged(view, oldScale, newScale); } //key事件未被加载时调用 @Override public void onUnhandledKeyEvent(WebView view, KeyEvent event) { super.onUnhandledKeyEvent(view, event); } };
将上面定义的WebViewClient设置给WebView:
mWebView.setWebViewClient(mWebViewClient);
4.WebChromeClient
WebChromeClient是辅助WebView处理Javascript的对话框,网站图标,网站title,加载进度条等。
方法中的代码都是有Android端自己处理。WebChromeClient mWebChromeClient = new WebChromeClient(){ //获得网页的加载进度 @Override public void onProgressChanged(WebView view, int newProgress) { if (newProgress <100){ String progress = newProgress+"%"; }else { } } //获取Web页中的title用来设置自己界面中的title //当加载出错的时候,比如无网络,这时onReceivedTitle中获取的标题为找不到该网页, //因此建议当触发onReceiveError时不时使用获取到的title @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) { // } //处理alert弹出框,html弹框的一种方式 @Override public boolean onJsAlert(WebView view, String url, String message, JsResult result) { return true; } //处理prompt弹出框 @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { return true; } //处理confirm弹出框 @Override public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { return true; } };
同样,将上面定义的WebChromeClient设置给WebView:
mWebView.setWebChromeClient(mWebChromeClient);
5.WebView的其他方法
(1)前进、后退:
mWebView.goBack();//后退 mWebView.goForward();//前进 //以当前的index为起始点前进或者后退到历史纪录中指定的steps, //如果steps为负数则后退,正数则为前进 mWebView.goBackOrForward(steps); mWebView.canGoBack();//是否可以后退 mWebView.canGoForward();//是否可以前进
(2)清除缓存数据:
//清除网页访问留下的缓存,由于内核缓存是全局的,因此这个方法不仅仅针对webview而是针对整个应用程序 mWebView.clearCache(true); //清除当前webView访问的历史纪录,只会清除webview访问历史纪录里的所有历史纪录除了当前访问记录 mWebView.clearHistory(); //这个API仅仅清除自动完成填充的表单数据,并不会清除webView清除到本地的数据 mWebView.clearFormData();
(3)webview的状态:
//激活WebView为活跃状态,能正常执行网页的响应 mWebView.onResume(); //当页面被失去焦点被切换到后台不可见状态,需要执行onPause动作,onPause动作通知内核暂停所有的动作,比如DOM的解析、plugin的执行、JavaScript执行。 mWebView.onPause(); 当应用程序被切换到后台我们使用了webview, 这个方法不仅仅针对当前的webview而是全局的全应用程序的webview,它会暂停所有webview的layout,parsing,javascripttimer。降低CPU功耗。 mWebView.pauseTimers(); 恢复pauseTimers时的动作。 mWebView.resumeTimers(); //销毁,关闭了Activity时,音乐或视频,还在播放。就必须销毁。 mWebView.destroy();
注意:webview调用destory时,webview仍绑定在Activity上,这是由于自定义构建时传入了该Activity的context对象,因此需要先从父容器中移除webview,然后再销毁webview。
RelativeLayout rootLayout = (RelativeLayout) findViewById(R.id.activity_main); rootLayout.removeView(mWebView); mWebView.destroy();
(4)判断WebView是否已经滚动到页面低端或者顶端:
- getScrollY():方法返回的是当前可见区域的顶端距整个页面顶端的距离,也就是当前内容滚动的距离;
- getHeight()或者getBottom():方法都返回当前WebView这个容器的高度;
- getContentHeight():返回的是整个html的高度,但并不等同于当前整个页面的高度,因为WebView有缩放功能,所以当前整个页面的高度实际上应该是原始html的高度再乘上缩放比例,因此,更正后的结果,准确的判断方法应该是:
if (mWebView.getContentHeight() * mWebView.getScale() == (mWebView.getHeight() + mWebView.getScrollY())){ //已经处于底端 } if (mWebView.getScrollY() == 0){ //处于顶端 }
(5)返回键
返回上一次浏览的页面:@Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK && mWebView.canGoBack()){ mWebView.goBack(); return true; } return super.onKeyDown(keyCode,event); }
(6)调用JS代码:(待续。。。)
首先在assets文件夹下创建一个html文档:js中调用本地方法
接下来是主要交互的java代码:
Button btn = (Button) findViewById(R.id.btn); mWebView = (WebView) findViewById(R.id.webview); WebSettings webSettings = mWebView.getSettings(); //设置编码 webSettings.setDefaultTextEncodingName("utf-8"); //支持JS webSettings.setJavaScriptEnabled(true); //设置背景颜色 透明 mWebView.setBackgroundColor(Color.argb(0, 0, 0, 0)); //设置本地调用对象及其接口 mWebView.addJavascriptInterface(new JavaScriptObject(JSActivity.this), "myObj"); //载入js mWebView.loadUrl("file:///android_asset/test.html"); //点击调用js中方法 btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mWebView.loadUrl("javascript:funFromjs()"); Toast.makeText(JSActivity.this, "调用javascript:funFromjs()", Toast.LENGTH_SHORT).show(); } });
最后是上面代码中用到的JavaScriptObject对象:
public class JavaScriptObject { private Context mContext; public JavaScriptObject(Context context){ this.mContext = context; } @JavascriptInterface public void fun1FromAndroid(String name) { Toast.makeText(mContext, name, Toast.LENGTH_LONG).show(); } @JavascriptInterface public void fun2(String name){ Toast.makeText(mContext, "调用fun2:"+name, Toast.LENGTH_SHORT).show(); }}
Android调用JS有一个漏洞:
(7)Android5.0 WebView中Http和Https混合问题:
在Android 5.0上webview默认不允许加载Http与Https混合内容: 解决办法:if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){ mWebView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); }
参数类型说明:
(1) MIXED_CONTENT_ALWAYS_ALLOW:允许从任何来源加载内容,即使起源是不安全的; (2)MIXED_CONTENT_NEVER_ALLOW:不允许Https加载Http的内容,即不允许从安全的起源去加载一个不安全的资源; (3)MIXED_CONTENT_COMPLTIBILITY_MODE:当涉及到混合式内容时,WebView会尝试去兼容最新Web浏览器的风格; 在5.0以下Android默认是全允许的。 但是到了5.0以上,就是不允许,实际情况下我们很难确定所有的网页都是Https的,所以我们就需要这一步的操作。分享:
(8)Cookie相关
同步cookie只需要获得CookieManager的对象将cookie设置进去就可以了。前提:从服务器的返回头中取出cookie根据Http请求的客户端不同,获取cookie的方式也不同,请自行获取。- 客户端通过一下代码设置cookie,如果两次设置相同,会覆盖上一次的。
/** *将cookie设置到webView * @param url 需要加载的url * @param cookie 要同步的cookie */ public static void syncCookie(String url,String cookie){ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP){ CookieSyncManager.createInstance(context); } CookieManager cookieManager = CookieManager.getInstance(); //如果没有特殊需求,这里只需要将session id以“key=value”形式作为cookie即可 cookieManager.setCookie(url,cookie); }
注意:
1.同步cookie要在WebView加载url之前,否则WebView无法获得相应的cookie,也就无法通过验证; 2.cookie应该被及时更新,否则很可能导致WebView拿的是就得session id和服务器进行通信。
- CookieManager会将这个Cookie存入该应用程序data/data/package_name/app_WebView/Cookies.db
- 打开网页,WebView从数据库中读取该cookie值,放到http请求的头部,传到服务器;
/** * 获取指定url的cookie */ public static String syncCookie(String url){ CookieManager cookieManager = CookieManager.getInstance(); return cookieManager.getCookie(url); }
- 清除Cookie:
//这两个在API level 21被抛弃 CookieManager.getInstance().removeSessionCookie(); CookieManager.getInstance().removeAllCookie(); //推荐使用这两个,level 21新加的 CookieManager.getInstance().removeSessionCookies();//移除所有过期的cookie CookieManager.getInstance().removeAllCookies();//移除所有的cookie
private void removeCookie(Context context){ CookieManager.getInstance().removeAllCookies(new ValueCallback() { @Override public void onReceiveValue(Boolean aBoolean) { //清除结果 } }); }
(9)避免WebView内存泄漏的一些方式
- 可以将WebView的Activity新起一个进程,结束的时候直接System.exit(0),退出当前进程; 启动新进程,主要代码:AndroidManifest.xml配置文件代码如下:
在新进程中启动Activity,里面传了一个Url:
Intent intent = new Intent("com.gjj.activity.htmlactivity"); Bundle bundle = new Bundle(); bundle.putString("url",url); intent.putExtra("bundle",bundle); startActivity(intent);
然后在Html5Activity的onDestroy()最后加上System.exit(0),杀死当前进程。
- 不能在xml中定义WebView,而是在需要的时候创建,并且Context使用getApplicationContext,如下代码:
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); mWebView = new WebView(getApplicationContext()); mWebView.setLayoutParams(params); mLayout.addView(mWebView);
- 在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(); }
参考文章: