29

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

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

4

5 回答 5

11

我意识到这个问题很老,但我遇到了它,所以我想我会在这里回答。简而言之 - 我发现 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 可以重写:

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

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

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

于 2016-10-05T17:03:15.837 回答
3

这里的答案在发布时是很好的答案,但现在 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);
 }
于 2021-09-08T22:38:47.737 回答
3

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

这个问题在stackoverflow中提出的想法非常有趣,如果你不能触摸那个webview的JS代码,那就更有趣了。

我描述了我们为能够通过 JS PostMessage API通过消息步骤将 webview 与本机应用程序通信所做的工作。

使用我们的 webview 实现。我们实现了onPageFinished方法,以便注入我们的 JS 代码来加载网页。

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
    }
于 2020-06-21T15:00:07.057 回答
2

我遇到了类似的问题,我想听听.postMessagewebview 中发生的事件。我在原始 html 中检查了事件是这样触发的

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

因此,我对 postMessage 进行了更多研究,并找到了添加 eventListener 的链接

window.addEventListener("message", receiveMessage, false);

function receiveMessage(event)
{
  if (event.origin !== "http://example.org:8080")
    return;

  // ...
}

durban 的 回答帮助我设置了 Android 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

然后在如下onPageStarted回调中调用javascriptWebViewClient

  @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);
  }
于 2020-10-19T14:14:32.603 回答
1

您需要使用中间抽象接口,在一个实现中通过 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
}
于 2015-04-19T22:45:53.420 回答