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文档:
<body>
<a>js中调用本地方法</a>
<script>
function funFromjs(){
document.getElementById("helloweb").innerHTML="Hello WebView,I'm from js"
}
var aTag = document.getElementsByTagName('a')[0];
aTag.addEventListener('click', function(){
//调用android本地方法
myObj.fun1FromAndroid("调用android本地方法fun1FromAndroid(String name)!!");
return false;
}, false);
</script>
<p></p>
<div id="helloweb">
</div>
</body>
接下来是主要交互的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有一个漏洞:
http://blog.csdn.net/leehong2005/article/details/11808557
(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<Boolean>() {
@Override
public void onReceiveValue(Boolean aBoolean) {
//清除结果
}
});
}
(9)避免WebView内存泄漏的一些方式
- 可以将WebView的Activity新起一个进程,结束的时候直接System.exit(0),退出当前进程;
启动新进程,主要代码:AndroidManifest.xml配置文件代码如下:
<activity android:name=".Html5Activity"
android:process=":gjj.process.web">
<intent-filter>
<action android:name="com.gjj.activity.htmlactivity"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
在新进程中启动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();
}
参考文章:
史上最全WebView使用,附送Html5Activity一份
轻松搞定WebView cookie同步问题