我通常使用 HTML5 PostMessage API将信息从 iframed 内容传递到父框架。最近,我在 Android WebView 中使用了我的内容(据我所知,这是 iframe 的原生 Android 等效项)。本机应用程序有没有办法监听我发送给他们的 PostMessage 事件?
我知道addJavascriptInterface存在,我只是希望有一种方法可以重用我现有的 PostMessage 代码而无需编写新的东西。
我通常使用 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 可以重写:
postMessage(parent, JSON.stringify({
action : 'openModal',
source : embedId
}), '*');
现在我们有一组代码可以在 iframe 或 android WebView 中运行而无需更改(至少对这部分代码而言)。
我希望这对某人有所帮助。
这里的答案在发布时是很好的答案,但现在 androidx.webkit 可用,我相信这是推荐的方法。
特别是,WebViewCompat.addWebMessageListener
将WebViewCompat.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 应用程序与来自第三方的外部 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
}
我遇到了类似的问题,我想听听.postMessage
webview 中发生的事件。我在原始 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);
}
您需要使用中间抽象接口,在一个实现中通过 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
}