我通常使用HTML5的PostMessage API将信息从我的嵌入式内容传递到父级框架。最近,我的内容被用在了Android WebView中(据我所知,这是与iframe等效的本地Android应用程序)。是否有一种方法让本地应用程序监听我发送给他们的PostMessage事件呢?
我知道存在addJavascriptInterface,但我希望能够重用我现有的PostMessage代码而不编写新代码。
我通常使用HTML5的PostMessage API将信息从我的嵌入式内容传递到父级框架。最近,我的内容被用在了Android WebView中(据我所知,这是与iframe等效的本地Android应用程序)。是否有一种方法让本地应用程序监听我发送给他们的PostMessage事件呢?
我知道存在addJavascriptInterface,但我希望能够重用我现有的PostMessage代码而不编写新代码。
我知道这个问题很旧,但我遇到了它,所以我想在这里回答一下。简而言之——我发现postMessage至少可以用于从子iframe与父窗口进行通信,但是......
结果我们发现Android的WebView中iframe的行为并不理想,因此我们直接渲染了iframe的内容(如你所建议的)。这给我们留下了两个问题——首先,我们有很多来自iframe的消息钩子与其父级之间的通信,其次,我们仍然需要调用Android来对这些事件做出反应。
以下是我们代码中的一个示例消息——它分散在整个iframe中:
parent.postMessage(JSON.stringify({
action : 'openModal',
source : embedId
}), '*');
当我们在Android上时,我们想要使用Android对JavaScript接口的支持,在WebView中运行时注入一个对象来处理此请求。android's support for javascript interfaces
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中运行而不需要更改的单一代码集(至少对于这部分代码而言)。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);
}
isFeatureSupported
部分的更多信息?这完全取决于System WebView APK,但是哪个版本呢?有没有办法要求特定版本的APK?我主要关注addWebMessageListener
的allowedOriginRules
,但如果没有支持,就不能让本地接口失败。 - themoondothshine.postMessage
事件。我在原始 HTML 中检查后发现该事件是这样触发的:
window.parent.postMessage(JSON.stringify(message), '*');
因此,我深入研究了 postMessage,并找到了 this link 来添加 eventListener。window.addEventListener("message", receiveMessage, false);
function receiveMessage(event)
{
if (event.origin !== "http://example.org:8080")
return;
// ...
}
首先,需要一个类似于这样的自定义类用于JavascriptInterface:
class JsObject {
@JavascriptInterface
public void receiveMessage(String data) {
Log.i("JsObject", "postMessage data="+data);
//handle data here
}
}
webView.addJavascriptInterface(new JsObject(), "Android"); //"Android" is just a name
WebViewClient
的 onPageStarted
回调中调用以下 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);
}
最近我们需要开发一个项目,需要将我们的原生Android应用与第三方提供的外部webview集成进行通信。
如果你无法接触那个webview的JS代码,那么这个问题在stackoverflow上引起了很大的兴趣。
我将描述我们所做的事情,以便通过JS PostMessage API消息步骤实现webview和本机应用程序之间的通信。
使用我们的webview实现。 我们实现了onPageFinished方法,以便注入我们的JS代码来加载Web。
override fun onPageFinished(url: String?) {
webview.loadUrl(
"javascript:(function() {" +
"window.parent.addEventListener ('message', function(event) {" +
" Android.receiveMessage(JSON.stringify(event.data));});" +
"})()"
)
}
webview.addJavascriptInterface(JsObject(presenter), "Android”)
class JsObject(val presenter: Presenter) {
@JavascriptInterface
fun receiveMessage(data: String): Boolean {
presenter.onDataReceived(data)
Log.d("Data from JS", data)
return true
}
您需要使用一个中间的抽象接口,在一种实现中通过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
}
window.addEventListener
)然后调用该对象-定义在该对象上适当处理你的信息的任何方法。我提供了另一个例子作为备选答案(https://dev59.com/7WIk5IYBdhLWcg3wkezM#39879882),其中包含更详细的说明。 - darrin