Android WebView常见问题解决方案汇总:

就目前而言,如何应对版本的频繁更新呢,又如何灵活多变地展示我们的界面呢,这又涉及到了web app与native app之间孰优孰劣的争论. 于是乎,一种混合型的app诞生了,灵活多变的部分,如淘宝商城首页的活动页面,一集凡客诚品中我们都可以见到web 页面与native页面的混合,既利用了web app的灵活易更新,也借助了native app本身的效率.
当然,就会用到webview这样的一个控件,这里,我把自己使用过程中遇到的一些问题整理下来.

首先上张图对WebView进行一个基本的回顾:

 


以上思维导图原文件下载地址:

http://download.csdn.net/detail/t12x3456/6509195

 

然后看一下具体的问题及解决方案:

1.为WebView自定义错误显示界面:

覆写WebViewClient中的onReceivedError()方法:

  1. /**
  2. * 显示自定义错误提示页面,用一个View覆盖在WebView
  3. */
  4. protected void showErrorPage() {
  5. LinearLayout webParentView = (LinearLayout)mWebView.getParent();
  6. initErrorPage();
  7. while (webParentView.getChildCount() > 1) {
  8. webParentView.removeViewAt(0);
  9. }
  10. LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT);
  11. webParentView.addView(mErrorView, 0, lp);
  12. mIsErrorPage = true;
  13. }
  14. protected void hideErrorPage() {
  15. LinearLayout webParentView = (LinearLayout)mWebView.getParent();
  16. mIsErrorPage = false;
  17. while (webParentView.getChildCount() > 1) {
  18. webParentView.removeViewAt(0);
  19. }
  20. }
  21. protected void initErrorPage() {
  22. if (mErrorView == null) {
  23. mErrorView = View.inflate(this, R.layout.online_error, null);
  24. Button button = (Button)mErrorView.findViewById(R.id.online_error_btn_retry);
  25. button.setOnClickListener(new OnClickListener() {
  26. public void onClick(View v) {
  27. mWebView.reload();
  28. }
  29. });
  30. mErrorView.setOnClickListener(null);
  31. }
  32. }
  1. @Override
  2. public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
  3. <span style="white-space:pre">                  </span>
  4. <span style="white-space:pre">          </span>mErrorView.setVisibility(View.VISIBLE);
  5. <span style="white-space:pre">          </span>super.onReceivedError(view, errorCode, description, failingUrl);
  6. }

2.WebView cookies清理:

  1. CookieSyncManager.createInstance(this);
  2. CookieSyncManager.getInstance().startSync();
  3. CookieManager.getInstance().removeSessionCookie();


3.清理cache 和历史记录:

  1. webView.clearCache(true);
  2. webView.clearHistory();

4.判断WebView是否已经滚动到页面底端:

  1. getScrollY()方法返回的是当前可见区域的顶端距整个页面顶端的距离,也就是当前内容滚动的距离.
  2. getHeight()或者getBottom()方法都返回当前WebView 这个容器的高度
  3. getContentHeight 返回的是整个html 的高度,但并不等同于当前整个页面的高度,因为WebView 有缩放功能, 所以当前整个页面的高度实际上应该是原始html 的高度再乘上缩放比例. 因此,更正后的结果,准确的判断方法应该是:
  4. if(WebView.getContentHeight*WebView.getScale() == (webview.getHeight()+WebView.getScrollY())){ //已经处于底端 }

5.URL拦截:

Android WebView是拦截不到页面内的fragment跳转的。但是url跳转的话,又会引起页面刷新,H5页面的体验又下降了。只能给WebView注入JS方法了。

 6.处理WebView中的非超链接请求(如Ajax请求): 

有时候需要加上请求头,但是非超链接的请求,没有办法再shouldOverrinding中拦截并用webView.loadUrl(String url,HashMap headers)方法添加请求头

目前用了一个临时的办法解决:

首先需要在url中加特殊标记/协议, 如在onWebViewResource方法中拦截对应的请求,然后将要添加的请求头,以get形式拼接到url末尾

在shouldInterceptRequest()方法中,可以拦截到所有的网页中资源请求,比如加载JS,图片以及Ajax请求等等

Ex:

  1. @SuppressLint("NewApi")
  2. @Override
  3. public WebResourceResponse shouldInterceptRequest(WebView view,String url) {
  4. // 非超链接(如Ajax)请求无法直接添加请求头,现拼接到url末尾,这里拼接一个imei作为示例
  5. String ajaxUrl = url;
  6. // 如标识:req=ajax
  7. if (url.contains("req=ajax")) {
  8. ajaxUrl += "&imei=" + imei;
  9. }
  10. return super.shouldInterceptRequest(view, ajaxUrl);
  11. }

7.在页面中先显示图片:

  1. @Override
  2. public void onLoadResource(WebView view, String url) {
  3. mEventListener.onWebViewEvent(CustomWebView.this, OnWebViewEventListener.EVENT_ON_LOAD_RESOURCE, url);
  4. if (url.indexOf(".jpg") > 0) {
  5. hideProgress(); //请求图片时即显示页面
  6. mEventListener.onWebViewEvent(CustomWebView.this, OnWebViewEventListener.EVENT_ON_HIDE_PROGRESS, view.getUrl());
  7. }
  8. super.onLoadResource(view, url);
  9. }

 8.屏蔽掉长按事件 因为webview长按时将会调用系统的复制控件:

  1. mWebView.setOnLongClickListener(new OnLongClickListener() {
  2. @Override
  3. public boolean onLongClick(View v) {
  4. return true;
  5. }
  6. });

9.在WebView加入 flash支持:

  1. String temp = "<html><body bgcolor=\"" + "black"
  2. + "\"> <br/><embed src=\"" + url + "\" width=\"" + "100%"
  3. + "\" height=\"" + "90%" + "\" scale=\"" + "noscale"
  4. + "\" type=\"" + "application/x-shockwave-flash"
  5. + "\"> </embed></body></html>";
  6. String mimeType = "text/html";
  7. String encoding = "utf-8";
  8. web.loadDataWithBaseURL("null", temp, mimeType, encoding, "");

10.WebView保留缩放功能但隐藏缩放控件:

  1. mWebView.getSettings().setSupportZoom(true);
  2. mWebView.getSettings().setBuiltInZoomControls(true);
  3. if (DeviceUtils.hasHoneycomb())
  4. mWebView.getSettings().setDisplayZoomControls(false);

注意:setDisplayZoomControls是在Android 3.0中新增的API.

这些是目前我整理出来的一些注意事项和问题解决方案,也欢迎大家多提一些关于webview的问题,如果有合适的解决方案,我会直接更新到这篇文章.

8月份更新:

11.WebView 在Android4.4的手机上onPageFinished()回调会多调用一次(具体原因待追查)

需要尽量避免在onPageFinished()中做业务操作,否则会导致重复调用,还有可能会引起逻辑上的错误.

12.需要通过获取Web页中的title用来设置自己界面中的title及相关问题:

需要给WebView设置 WebChromeClient,并在onReceiveTitle()回调中获取

  1. WebChromeClient webChromeClient = new WebChromeClient() {
  2. @Override
  3. public void onReceivedTitle(WebView view, String title) {
  4. super.onReceivedTitle(view, title);
  5. txtTitle.setText(title);
  6. }
  7. };

但是发现在小米3的手机上,当通过webview.goBack()回退的时候,并没有触发onReceiveTitle(),这样会导致标题仍然是之前子页面的标题,没有切换回来.

这里可以分两种情况去处理:

(1) 可以确定webview中子页面只有二级页面,没有更深的层次,这里只需要判断当前页面是否为初始的主页面,可以goBack的话,只要将标题设置回来即可.

(2)webview中可能有多级页面或者以后可能增加多级页面,这种情况处理起来要复杂一些:

因为正常顺序加载的情况onReceiveTitle是一定会触发的,所以就需要自己来维护webview  loading的一个url栈及url与title的映射关系

那么就需要一个ArrayList来保持加载过的url,一个HashMap保存url及对应的title.

正常顺序加载时,将url和对应的title保存起来,webview回退时,移除当前url并取出将要回退到的web 页的url,找到对应的title进行设置即可.

这里还要说一点,当加载出错的时候,比如无网络,这时onReceiveTitle中获取的标题为 找不到该网页,因此建议当触发onReceiveError时,不要使用获取到的title.

13.WebView因addJavaScriptInterface()引起的安全问题.

这个问题主要是因为会有恶意的js代码注入,尤其是在已经获取root权限的手机上,一些恶意程序可能会利用该漏洞安装或者卸载应用.

关于详细的情况可以参考下面这篇文章:

.http://blog.csdn.net/leehong2005/article/details/11808557

还有一个开源项目可以参考: https://github.com/pedant/safe-java-js-webview-bridge, 该项目利用onJsPrompt() 替代了addJavaScriptInterface(),(解决方案类似上述参考的博客)同时增加了异步回调,

很好地解决了webview  js注入的安全问题.

10月份更新:

14.WebView页面中播放了音频,退出Activity后音频仍然在播放

需要在Activity的onDestory()中调用

  1. webView.destroy();

但是直接调用可能会引起如下错误:

  1. 10-10 15:01:11.402: E/ViewRootImpl(7502): sendUserActionEvent() mView == null
  2. 10-10 15:01:26.818: E/webview(7502): java.lang.Throwable: Error: WebView.destroy() called while still attached!
  3. 10-10 15:01:26.818: E/webview(7502):    at android.webkit.WebViewClassic.destroy(WebViewClassic.java:4142)
  4. 10-10 15:01:26.818: E/webview(7502):    at android.webkit.WebView.destroy(WebView.java:707)
  5. 10-10 15:01:26.818: E/webview(7502):    at com.didi.taxi.ui.webview.OperatingWebViewActivity.onDestroy(OperatingWebViewActivity.java:236)
  6. 10-10 15:01:26.818: E/webview(7502):    at android.app.Activity.performDestroy(Activity.java:5543)
  7. 10-10 15:01:26.818: E/webview(7502):    at android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1134)
  8. 10-10 15:01:26.818: E/webview(7502):    at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:3619)
  9. 10-10 15:01:26.818: E/webview(7502):    at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3654)
  10. 10-10 15:01:26.818: E/webview(7502):    at android.app.ActivityThread.access$1300(ActivityThread.java:159)
  11. 10-10 15:01:26.818: E/webview(7502):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1369)
  12. 10-10 15:01:26.818: E/webview(7502):    at android.os.Handler.dispatchMessage(Handler.java:99)
  13. 10-10 15:01:26.818: E/webview(7502):    at android.os.Looper.loop(Looper.java:137)
  14. 10-10 15:01:26.818: E/webview(7502):    at android.app.ActivityThread.main(ActivityThread.java:5419)
  15. 10-10 15:01:26.818: E/webview(7502):    at java.lang.reflect.Method.invokeNative(Native Method)
  16. 10-10 15:01:26.818: E/webview(7502):    at java.lang.reflect.Method.invoke(Method.java:525)
  17. 10-10 15:01:26.818: E/webview(7502):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1187)
  18. 10-10 15:01:26.818: E/webview(7502):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
  19. 10-10 15:01:26.818: E/webview(7502):    at dalvik.system.NativeStart.main(Native Method)

如上所示,webview调用destory时,webview仍绑定在Activity上.这是由于自定义webview构建时传入了该Activity的context对象,因此需要先从父容器中移除webview,然后再销毁webview:

  1. rootLayout.removeView(webView);
  2. webView.destroy();

15. WebView长按自定义菜单,实现复制分享相关功能

这个功能首先可以从两方面完成:

(1) 在js中完成:

处理android.selection.longTouch

这里推荐一个开源项目进行参考,:

https://github.com/btate/BTAndroidWebViewSelection

   (2) 安卓层处理:

首先使用OnTouchListener实现长按实现监听,然后实现WebView的Context menu,最后调用webview中的emulateShiftHeld(),为了适配安卓不同版本,最好使用反射方式调用.

Android WebView常见问题及解决方案汇总的更多相关文章

  1. Android之Android WebView常见问题及解决方案汇总

    如有转载,请声明出处: 时之沙: http://blog.csdn.net/t12x3456 Android WebView常见问题解决方案汇总: 就目前而言,如何应对版本的频繁更新呢,又如何灵活多变 ...

  2. Android WebView常见问题及解决方案汇总【很全很实用】

    http://www.cnblogs.com/olartan/p/5713013.html

  3. Android WebView常见问题的解决方案总结----例如Web page not available

    之前android虚拟机一直都可以直接联网,今天写了一个WebView之后,突然报出了Web page not available的错误,但是查看虚拟机自带的浏览器,是可以上网的,所以检查还是代码的问 ...

  4. Android WebView常见问题解决方案汇总

    问题目录: 1.为WebView自定义错误显示界面: 2.WebView cookies清理 3.清理cache 和历史记录 4.判断WebView是否已经滚动到页面底端 5.URL拦截 6.处理We ...

  5. Android WebView 常见问题

    1.为WebView自定义错误显示界面: /** * 显示自定义错误提示页面,用一个View覆盖在WebView */ protected void showErrorPage() { LinearL ...

  6. android WebView交互优化

    安卓的WebView一般是嵌套在activity或者fragment中的,但是如果在这种activity页面上点击返回按钮,一般会finish掉当前activity.其实是应该关闭当前的WebView ...

  7. android webview开发问题及优化汇总

    我们在native与网页相结合开发的过程中,难免会遇到关于WebView一些共通的问题.就我目前开发过程中遇到的问题以及最后得到的优化方案都将在这里列举出来.有些是老生常谈,有些则是个人摸索得出解决方 ...

  8. Android Webview SSL 自签名安全校验解决方案

    服务器证书校验主要针对 WebView 的安全问题. 在 app 中需要通过 WebView 访问 url,因为服务器采用的自签名证书,而不是 ca 认证,使用 WebView 加载 url 的时候会 ...

  9. Android Studio使用过程中常见问题及解决方案

    熟悉Android的童鞋应该对Android Studio都不陌生.Android编程有两个常用的开发环境,分别是Android Studio和Eclipse,之前使用比较多的是Eclipse,而现在 ...

随机推荐

  1. Hive 实现HBase 数据批量插入

    HBase 数据的插入可以使用Java API 来写Java 程序逐条倒入,但是不是很方便.利用Hive自带的一个Jar包,可以建立Hive和HBase的映射关系 利用Hive 的insert可以将批 ...

  2. java读取utf8配置文件乱码

    email.properties文件如果以ISO-8859-1编码,那么以下的java代码读取中文不会乱码,因为eclipse下中文都被翻译成/u... //in Conf.javaPropertie ...

  3. poj2386(简单dfs)

    就是求图中有多少个水洼.对图进行dfs遍历,并把是水洼的地方全部标记.然后从下一个是水哇的地方再进行dfs. #include <cstdio> #include <iostream ...

  4. hadoop2.2.0 + hbase 0.94 + hive 0.12 配置记录

    一开始用hadoop2.2.0 + hbase 0.96 + hive 0.12 ,基本全部都配好了.只有在hive中查询hbase的表出错.以直报如下错误: java.io.IOException: ...

  5. Codeforces Beta Round #7

    A题,应该是水题,我没看懂... B题,这题很多坑,注意x是LL,而且x = 0的情况,所以初始化要为-1,最后这题是内存管理啊..操作系统学的不好,题意读不懂啊. 申请内存的时候,是从头找 如果这一 ...

  6. CF 16C. Monitor

    题目链接 水题依旧无法1Y. #include <cstdio> #include <iostream> #include <cmath> using namesp ...

  7. POJ 2955 Brackets(区间DP)

    题目链接 #include <iostream> #include <cstdio> #include <cstring> #include <vector& ...

  8. 直接用Qt写soap

    直接用Qt写soap 最近的项目里用到了webservice, 同事用的是`gSoap`来搞的. 用这个本身没什么问题, 但这货生成的代码实非人类可读, 到处都是`__`和`_`, 看得我眼晕.... ...

  9. ThinkPad_T430重装系统

    联想thinkpad T430为T系列的旗舰级产品,全新的ThinkPad T430将该系列坚固的机身.稳定高效的散热表现.超强的易用性.不俗的性能以及出色的操控感受等优点完美的继承下来,始终坚持把每 ...

  10. GO语言练习:for基本用法

    1.代码 2.运行 1.代码 package main import "fmt" func main(){ ; k < ; k++{ JLoop: ; j < ; j+ ...