PostMessage API 是否可用于与 Android WebView 通信

Can the PostMessage API be used to communicate with an Android WebView?

本文关键字:Android WebView 通信 用于 API 是否 PostMessage      更新时间:2023-09-26

我通常使用 HTML5 PostMessage API 将信息从我的 iframed 内容传达到父框架。最近,我的内容在Android WebView中使用(据我所知,这是iframe的原生Android等价物(。本机应用程序有没有办法侦听我发送给他们的 PostMessage 事件?

我知道addJavascriptInterface存在,我只是希望有一种方法可以在不编写新东西的情况下重用我现有的PostMessage代码。

我知道这个问题很旧,但我遇到了它,所以我想我会在这里回答。简而言之 - 我发现 postMessage 至少适用于从子 iframe 到父窗口的通信,但是......

事实证明,我们真的不喜欢iframe在android的WebView中的行为方式,所以我们直接渲染了iframe的内容(如你所建议的(。这给我们留下了两个问题 - 首先,我们有很多从iframe到它的父级的消息钩子,其次我们仍然需要调用android来对这些事件做出反应。

下面是来自我们代码的示例消息 - 它散布在整个 iframe 中:

    parent.postMessage(JSON.stringify({
        action    : 'openModal',
        source    : embedId
    }), '*');

当我们在Android上时,我们想要的是使用android对javascript接口的支持来注入一个对象,以便在WebView中运行时处理此请求。

在Android方面,这将看起来像这样:

class JsObject {
   @JavascriptInterface
    public boolean postMessage(String json, String transferList) {
        return false; // here we return true if we handled the post.
    }
}
// And when initializing the webview... 
webView.addJavascriptInterface(new JsObject(), "totDevice");

现在,当在此 WebView 中运行时,totDevice将存在,而在 iframe 中运行时,它将不存在。所以现在我们可以创建一个包装器来检查这种情况,并在两个方法之间干净地切换,而不是直接调用parent.postMessage。在这里,我们还在 Android 实现中添加了一个布尔开关,以防您只想处理某些消息:

function postMessage(parent, json, transferlist) {
    if (!totDevice || !totDevice.postMessage(json, transferList)) {
        parent.postMessage(json, transferlist);
    }
}

我们的原始帖子上面的消息可以重写:

    postMessage(parent, JSON.stringify({
        action    : 'openModal',
        source    : embedId
    }), '*');

现在我们有一组代码可以在iframe或android WebView中运行,而无需更改(至少对这部分代码而言(。

我希望这对某人有所帮助。

这里的答案在发布时是很好的答案,但现在androidx.webkit可用,我相信这是推荐的方法。

特别是,WebViewCompat.addWebMessageListenerWebViewCompat.postWebMessage将是javascript PostMessage API的对应项。

下面是从文档中复制的代码示例:

 // Web page (in JavaScript)
 myObject.onmessage = function(event) {
   // prints "Got it!" when we receive the app's response.
   console.log(event.data);
 }
 myObject.postMessage("I'm ready!");
 
 // App (in Java)
 WebMessageListener myListener = new WebMessageListener() {
   @Override
   public void onPostMessage(WebView view, WebMessageCompat message, Uri sourceOrigin,
            boolean isMainFrame, JavaScriptReplyProxy replyProxy) {
     // do something about view, message, sourceOrigin and isMainFrame.
     replyProxy.postMessage("Got it!");
   }
 };
 if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER)) {
   WebViewCompat.addWebMessageListener(webView, "myObject", rules, myListener);
 }

最近,我们不得不开发一个项目,该项目需要将我们的原生 Android 应用程序与来自第三方的外部 Web 视图集成进行通信。

这个问题在stackoverflow中提出的想法非常有趣,如果你不能触摸该web视图的JS代码,那就更是如此。

我描述了我们做了什么,以便能够通过 JS PostMessage API 的消息步骤与本机应用程序进行 Web 视图通信。

使用我们的网络视图实现。我们实现了onPageDone方法,以便注入我们的JS代码来加载Web。

override fun onPageFinished(url: String?) {
webview.loadUrl(
"javascript:(function() {" +
    "window.parent.addEventListener ('message', function(event) {" +
    " Android.receiveMessage(JSON.stringify(event.data));});" +
    "})()"
)
}

基本上,我们正在做的是创建一个侦听器,将这些消息发送到我们自己的JS和Android Bridge接口。我们之前在Android活动的webview设置中创建的,就像我们通常使用addJavascriptInterface所做的那样。

webview.addJavascriptInterface(JsObject(presenter), "Android”)

这样,我们已经有了该通信桥,并且postMessage发送的所有消息都将在该侦听器订阅的接口中到达我们。

class JsObject(val presenter: Presenter) {
    @JavascriptInterface
    fun receiveMessage(data: String): Boolean {
        presenter.onDataReceived(data)
        Log.d("Data from JS", data)
        return true
    }

我遇到了类似的问题,我想听听网络视图中发生的.postMessage事件。我在原始 html 中检查了事件是否像这样触发

window.parent.postMessage(JSON.stringify(message), '*');

因此,我对postMessage进行了更多挖掘,并找到了用于添加事件监听器的链接

window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
  if (event.origin !== "http://example.org:8080")
    return;
  // ...
}

德班的回答帮助我建立了安卓JS界面。

首先是像这样的JavascriptInterface的自定义类

class JsObject {
    @JavascriptInterface
    public void receiveMessage(String data) {
        Log.i("JsObject", "postMessage data="+data);
        //handle data here
    }
}

在加载url/html之前将javascriptInterface添加到webview。

webView.addJavascriptInterface(new JsObject(), "Android"); //"Android" is just a name

然后在WebViewClientonPageStarted回调中调用javascript,如下所示

  @Override
  public void onPageStarted(WebView view, String url, Bitmap favicon) {
        webView.loadUrl("javascript:(function() {" +
                            "function receiveMessage(event) {'n" +
                            "Android.receiveMessage(JSON.stringify(event.data));'n" +
                            "}" +
                            "window.addEventListener('"message'", receiveMessage, false);"+
                            "})()"
        );
        Log.i(TAG, "onPageStarted "+url);
  }

你需要使用一个中间抽象接口,在一个实现中通过PostMessage处理消息,在另一个情况下通过addJavascriptInterface处理消息。

window.addEventListener("message", onReceivedPostMessage, false);
function onReceivedPostMessage(event){
     //..ex deconstruct event into action & params
     var action = event.data.action;
     var params = event.data.params;
     performAction(action, params); //performAction would be the uniform API
}
function onReceivedActivityMessageViaJavascriptInterface(json){
     //..ex deconstruct data into action & params
     var data = JSON.parse(json); 
     var action = data.action;
     var params = data.params;
     performAction(action, params); //performAction would be the uniform API
}