Можно ли использовать API PostMessage для связи с Android WebView?
Я обычно использую HTML5 PostMessage API для передачи информации из моего содержимого iframed в родительский фрейм. Недавно мой контент использовался в Android WebView (насколько я могу судить, это эквивалент нативной версии iframe для Android). Есть ли способ для нативного приложения прослушивать события PostMessage, которые я им отправляю?
Я знаю, что addJavascriptInterface существует, я просто надеюсь, что есть способ повторно использовать мой существующий код PostMessage без написания чего-то нового.
5 ответов
Я понимаю, что этот вопрос старый, но я столкнулся с ним, поэтому решил, что отвечу здесь. Короче говоря - я обнаружил, что postMessage работает по крайней мере для связи от дочернего iframe до родительского окна, НО...
Оказывается, нам действительно не понравилось, как ведет себя iframe в WebView Android, поэтому вместо этого мы визуализировали содержимое iframe напрямую (как вы предлагаете). Это оставило нас с двумя проблемами: во-первых, у нас было много хуков обмена сообщениями от этого iframe до его родителя, а во-вторых, нам все еще нужно было обратиться к Android, чтобы отреагировать на эти события.
Вот пример сообщения из нашего кода, который был разбросан по всему iframe:
parent.postMessage(JSON.stringify({
action : 'openModal',
source : embedId
}), '*');
Когда мы находимся на Android, мы хотим использовать [поддержку андроида для интерфейсов javascript] ( https://developer.android.com/reference/android/webkit/WebView.html, java.lang.String)) для вставки объекта для обработки этого запроса при запуске в 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.addWebMessageListener
а также WebViewCompat.postWebMessage
будет аналогом API PostMessage javascript.
Вот пример кода, скопированный из документации:
// 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);
}
У меня была аналогичная проблема, я хотел послушать
.postMessage
событие, происходящее в веб-просмотре. Я проверил в необработанном 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;
// ...
}
durbon Ответ durbon помог мне настроить Android JS Interface.
Во-первых, собственный класс для JavascriptInterface, подобный этому
class JsObject {
@JavascriptInterface
public void receiveMessage(String data) {
Log.i("JsObject", "postMessage data="+data);
//handle data here
}
}
Добавьте javascriptInterface в веб-просмотр перед загрузкой url/html
webView.addJavascriptInterface(new JsObject(), "Android"); //"Android" is just a name
Затем вызовите javascript в
onPageStarted
обратный вызов
WebViewClient
как показано ниже
@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 с внешней интеграцией веб-просмотра от третьей стороны.
Идея, возникающая в связи с этим вопросом в stackru, очень интересна, особенно если вы не можете коснуться JS-кода этого веб-представления.
Я описываю, что мы сделали, чтобы иметь возможность передавать веб-просмотр в собственное приложение через шаги сообщения через JS PostMessage API.
Используя нашу реализацию 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, как обычно мы делаем с 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, а в другом случае - через 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
}