Android中WebView的基本配置与填坑记录大全
前言
在应用程序开发过程中,经常会采用webview来展现某些界面,这样就可以不受发布版本控制,实时更新,遇到问题可以快速修复。
但是在Android开发中,由于Android版本分化严重,每一个版本针对webview都有部分更改,因此在开发过程中会遇到各种各样的坑,下面这篇就来给大家介绍关于Android中WebView的基本配置与填坑记录,话不多说了,来一起看看详细的介绍吧。
基本配置
// 硬件加速 getActivity().getWindow().setFlags( WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); // WebView 配置 WebSettings webSettings = mWebView.getSettings(); // 生命周期 mWebView.onPause(); // 通过 onPause 动作通知内核暂停所有的动作,如 DOM 的解析、plugin 的执行、JavaScript 执行等 mWebView.onResume(); // 恢复 WebView,能正常执行网页的响应 ((ViewGroup) mWebView.getParent()).removeView(mWebView); mWebView.destroy(); // 当 Activity 要 destroy 时,应先将 WebView 移除,再 destroy 掉 // 前进后退 if (mWebView.canGoBack()) { mWebView.goBack(); } if (mWebView.canGoForward()) { mWebView.goForward(); } // 缓存相关 mWebView.clearCache(true); // 清除缓存 mWebView.clearHistory(); // 清除历史 mWebView.clearFormData(); // 清除表单数据 webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);// 设置缓存模式 // 缓存模式 LOAD_DEFAULT: 默认,根据 cache-control 决定是否从网络上取数据 LOAD_NORMAL: API level 17 中已经废弃, 从API level 11开始作用同 LOAD_DEFAULT 模式 LOAD_CACHE_ELSE_NETWORK: 只要本地有,无论是否过期,或者 no-cache,都使用缓存中的数据 LOAD_NO_CACHE: 不使用缓存,只从网络获取数据 LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据 // js 相关 webSettings.setJavaScriptEnabled(true); // 支持 js。如果碰到后台无法释放 js 导致耗电,应在 onStop 和 onResume 里分别设成 false 和 true mWebView.addJavascriptInterface(new WebAppInterface(this), "android"); // js 接口 webSettings.setPluginsEnabled(true); // 支持插件 // 设置自适应屏幕,两者合用 webSettings.setUseWideViewPort(true); // 将图片调整到适合 WebView 的大小 webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小 // 缩放操作 webSettings.setSupportZoom(true); // 支持缩放,默认为 true webSettings.setBuiltInZoomControls(true); // 设置内置的缩放控件,若为 false,则该 WebView 不可缩放 webSettings.setDisplayZoomControls(false); // 隐藏原生的缩放控件
填坑记录
1、WebViewClient 类常用方法
mWebView.setWebViewClient(new MyWebViewClient());
shouldOverrideUrlLoading()
在网页上的所有加载都经过这个方法,这个函数我们可以做很多操作。
onPageStarted()
开始载入页面调用的,我们可以设定一个 loading 的页面,告诉用户程序在等待网络响应。
onPageFinished()
在页面加载结束时调用。我们可以关闭 loading 条,切换程序动作。
onLoadResource()
在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次。
onReceivedError()
加载页面出现错误时调用。
2、WebChromeClient 类常用方法
mWebView.setWebChromeClient(new MyWebChromeClient());
onProgressChanged()
获得网页的加载进度并显示。
onReceivedTitle()
获取 Web 页中的标题。
onJsAlert()
支持 javascript 的警告框。
onJsConfirm()
支持 javascript 的确认框。
onJsPrompt()
支持 javascript 输入框。
3、 Https 和 Http 混合模式
从 Android 5.0 开始,WebView 默认不支持同时加载 Https 和 Http 资源。
解决方法:在webview加载页面之前,设置加载模式为 MIXED_CONTENT_ALWAYS_ALLOW
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); }
4、安全问题
addJavascriptInterface
Android 4.2 以前,要采用拦截 prompt() 的方式进行漏洞修复;Android 4.2 以后,只需要对被调用的函数以 @JavascriptInterface 进行注解。
searchBoxJavaBridge_、accessibility、accessibilityTraversal
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { mWebView.removeJavascriptInterface("searchBoxJavaBridge_"); mWebView.removeJavascriptInterface("accessibility"); mWebView.removeJavascriptInterface("accessibilityTraversal"); }
密码明文存储漏洞
webSettings.setSavePassword(false);
5、替换 WebView 的加载错误页面
@Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { super.onReceivedError(view, errorCode, description, failingUrl); showCustErrorPage(); }
private View mErrorView; protected void showCustErrorPage() { // 移除WebView ViewGroup webParentView = (ViewGroup) mWebViewInstance.getParent(); while (webParentView.getChildCount() > 0) { webParentView.removeViewAt(0); } // 生成自定义错误页面 if (mErrorView == null) { mErrorView = View.inflate(mContext, R.layout.webview_error, null); mErrorView.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { showWebViewPage(); mWebViewInstance.reload(); } }); } // 替换为错误页面 ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams( ViewPager.LayoutParams.FILL_PARENT, ViewPager.LayoutParams.FILL_PARENT); webParentView.addView(mErrorView, 0, lp); } protected void showWebViewPage() { // 移除自定义错误页面 ViewGroup webParentView = (ViewGroup) mErrorView.getParent(); while (webParentView.getChildCount() > 0) { webParentView.removeViewAt(0); } // 替换为WebView ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams( ViewPager.LayoutParams.FILL_PARENT, ViewPager.LayoutParams.FILL_PARENT); webParentView.addView(mWebViewInstance, 0, lp); }
6、WebView的内存泄露。
关于这个问题,我很难给你一个清晰的描述,你在谷歌里搜 webview lead memory 能搜到很多结果 甚至还有给谷歌提交的issue 哈哈,我也无法给出一个清晰的答案 在什么时候 什么版本那些手机上一定会出现内存泄露,
但是根据我自己的monkey结果来看,有时,webview内存泄露的情况还是很严重的,尤其是当你加载的页面比较庞大的时候。解决方案 我查了很多也用了很多,但是都不太理想,最后看了下微信和qq的做法,试了一下是目前效果最好的,
就是 当你要用webview的时候,记得最好 另外单独开一个进程 去使用webview 并且当这个 进程结束时,请手动调用System.exit(0)。
这是目前对于webview 内存泄露 最好的解决方案。使用此方法 所有因为webview引发的 资源无法释放等问题 全部可以解决。
7、getSettings().setBuiltInZoomControls(true) 引发的crush。
这个方法调用以后 如果你触摸屏幕 弹出那个提示框还没消失的时候 你如果activity结束了 就会报错了。3.0以上 4.4以下很多手机会出现这种情况
所以为了规避他,我们通常是在activity的onDestroy方法里手动的将webiew设置成 setVisibility(View.GONE)
8、onPageFinished 函数到底有用没有?
多数开发者都是参考的http://stackoverflow.com/questions/3149216/how-to-listen-for-a-webview-finishing-loading-a-url-in-android 这个上面的高票答案。
但其实根据我自己观察,这个函数并没有什么卵用,有的时候是提前结束,有的时候就迟迟无法结束,你信这个函数 还不如信上帝,甚至于onProgressChanged这个函数
都比onPageFinished 要准一些。如果你的产品经理坚持你一定要实现这种功能的话,我建议你 提早结束他,否则卡在那用户迟迟动不了 这种体验不好。
有空的同学可以跟一下源码,onPageFinished 在不同的内核里 调用的时机都不一样。说实话 我也很醉。。。这个问题 有完美解决方案的 请知会我一下。。。
9、后台无法释放js 导致耗电。
这个可能很少有人知道,我也是被投诉过 才了解,在有的手机里,你如果webview加载的html里 有一些js 一直在执行比如动画之类的东西,如果此刻webview 挂在了后台
这些资源是不会被释放 用户也无法感知。。。导致一直占有cpu 耗电特别快,所以大家记住了,如果遇到这种情况 请在onstop和onresume里分别把setJavaScriptEnabled();
给设置成false和true。
10、如果实在不想用开额外进程的方式解决webview 内存泄露的问题,那么下面的方法很大程度上可以避免这种情况
public void releaseAllWebViewCallback() { if (android.os.Build.VERSION.SDK_INT < 16) { try { Field field = WebView.class.getDeclaredField("mWebViewCore"); field = field.getType().getDeclaredField("mBrowserFrame"); field = field.getType().getDeclaredField("sConfigCallback"); field.setAccessible(true); field.set(null, null); } catch (NoSuchFieldException e) { if (BuildConfig.DEBUG) { e.printStackTrace(); } } catch (IllegalAccessException e) { if (BuildConfig.DEBUG) { e.printStackTrace(); } } } else { try { Field sConfigCallback = Class.forName("android.webkit.BrowserFrame").getDeclaredField("sConfigCallback"); if (sConfigCallback != null) { sConfigCallback.setAccessible(true); sConfigCallback.set(null, null); } } catch (NoSuchFieldException e) { if (BuildConfig.DEBUG) { e.printStackTrace(); } } catch (ClassNotFoundException e) { if (BuildConfig.DEBUG) { e.printStackTrace(); } } catch (IllegalAccessException e) { if (BuildConfig.DEBUG) { e.printStackTrace(); } } } }
在webview的 destroy方法里 调用这个方法就行了。
11、另外很多人 不知道webview 实际上有自己一套完整的cookie机制的,利用好这个 可以大大增加对客户端的访问速度。
实际上cookie就是存放在这个表里的。
很多人都想要一个效果:网页更新cookie 设置完cookie以后 不刷新页面即可生效。这个在2.3以下和2.3以上要实现的方法不太一样,所以要做一次兼容
public void updateCookies(String url, String value) { if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) { // 2.3及以下 CookieSyncManager.createInstance(getContext().getApplicationContext()); } CookieManager cookieManager = CookieManager.getInstance(); cookieManager.setAcceptCookie(true); cookieManager.setCookie(url, value); if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) { CookieSyncManager.getInstance().sync(); } }
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。