How Do You Use WebMessagePort As An Alternative to addJavascriptInterface()?

Google's security guidelines for Android app developers has the following:

WebViews do not use addJavaScriptInterface() with untrusted content.

On Android M and above, HTML message channels can be used instead.

Step #1: Populate your WebView using loadDataWithBaseURL()loadUrl() will not work, because bugs. You need to use an http or https URL for the first parameter to loadDataWithBaseURL() (or, at least, not file, because bugs). And you will need that URL later, so hold onto it (e.g., private static final String value).

Step #2: Decide when you want to initialize the communications from the JavaScript into Java. With addJavascriptInterface(), this is available immediately. However, using WebMessagePort is not nearly so nice. In particular, you cannot attempt to initialize the communications until the page is loaded (e.g., onPageFinished() on a WebViewClient).

Step #3: At the time that you want to initialize those communications, call createWebMessageChannel()on the WebView, to create a WebMessagePort[]. The 0th element in that array is your end of the communications pipe, and you can call setWebMessageCallback() on it to be able to respond to messages sent to you from JavaScript.

Step #4: Hand the 1st element in that WebMessagePort[] to the JavaScript by wrapping it in a WebMessage and calling postWebMessage() on the WebViewpostWebMessage() takes a Uri as the second parameter, and this Uri must be derived from the same URL that you used in Step #1 as the base URL for loadDataWithBaseURL().

  @TargetApi(Build.VERSION_CODES.M)
  private void initPort() {
    final WebMessagePort[] channel=wv.createWebMessageChannel();

    port=channel[0];
    port.setWebMessageCallback(new WebMessagePort.WebMessageCallback() {
      @Override
      public void onMessage(WebMessagePort port, WebMessage message) {
        postLux();
      }
    });

    wv.postWebMessage(new WebMessage("", new WebMessagePort[]{channel[1]}),
          Uri.parse(THIS_IS_STUPID));
  }

(where wv is the WebView and THIS_IS_STUPID is the URL used with loadDataWithBaseURL())

Step #5: Your JavaScript can assign a function to the global onmessage event, which will be called when postWebMessage() is called. The 0th element of the ports array that you get on the event will be the JavaScript end of the communications pipe, and you can stuff that in a variable somewhere. If desired, you can assign a function to onmessage for that port, if the Java code will use the WebMessagePort for sending over future data.

Step #6: When you want to send a message from JavaScript to Java, call postMessage() on the port from Step #5, and that message will be delivered to the callback that you registered with setWebMessageCallback() in step #3.

var port;

function pull() {
    port.postMessage("ping");
}

onmessage = function (e) {
    port = e.ports[0];

    port.onmessage = function (f) {
        parse(e.data);
    }
}

This sample app demonstrates the technique. It has a WebView that shows the current light level based on the ambient light sensor. That sensor data is fed into the WebView either on a push basis (as the sensor changes) or on a pull basis (user taps the "Light Level" label on the Web page). This app uses WebMessagePort for these on Android 6.0+ devices, though the push option is commented out so you can confirm that the pull approach is working through the port. I will have more detailed coverage of the sample app in an upcoming edition of my book.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值