¿Cómo se utiliza WebMessagePort como una alternativa a addJavascriptInterface ()?
Las directrices de seguridad de Google para los desarrolladores de aplicaciones para Android tienen lo siguiente:
WebViews no utiliza
addJavaScriptInterface()
con contenido no confiable.
- CrossWalk - Aceptar encabezado de idioma
- JavaScript createObjectURL no funciona en el navegador de Android
- Use caracteres especiales en strings.xml para cargar en WebView
- Cómo saber cuando se ha terminado la renderización de WebView
- ¿Mostrar anuncios nativos dentro del contenido de WebView en Android?
En Android M y superiores, los canales de mensajes HTML se pueden utilizar en su lugar.
Cerca de lo que puedo decir, "canales de mensajes HTML" se refiere a cosas como createWebMessageChannel()
, WebMessagePort
, WebMessage
y parentescos.
Sin embargo, no proporcionan ningún ejemplo. Todo lo que hacen es vincular a una especificación WhatWG , que es bastante poco clara. Y, basado en una búsqueda de Google para createWebMessageChannel
, parece que esto no se ha utilizado mucho todavía – mi blog que describe los cambios en el SDK de Android 6.0 hace que los 10 primeros resultados de búsqueda, y sólo lo menciono de pasada.
addJavascriptInterface()
se utiliza para permitir que JavaScript en un WebView
llame al código Java proporcionado por la aplicación mediante WebView
. ¿Cómo usaríamos "canales de mensajes HTML" como un reemplazo para eso?
- A veces lanza Error no detectado: método de llamada de error en NPObject en Android
- Uso de Internet (uso de datos) por WebView
- Cargando url con pdf en monodroid webview
- ¿Cómo hacer independientemente desplazamiento de filas y columnas (a la aplicación Netflix) en Android en un WebView?
- Android: Raphaeljs no está trabajando en webview
- ¿Cómo obtenerContentHeight cuando se utiliza loadData?
- El reproductor de video de youtube superpone el otro diseño cuando nos desplazamos
- ¿Cómo puedo validar un android.net.http.SsLCertificate con un X509TrustManager?
OK, tengo este trabajo, aunque es un poco malo.
Paso # 1: loadDataWithBaseURL()
su WebView
usando loadDataWithBaseURL()
. loadUrl()
no funcionará, porque los errores . Es necesario utilizar una URL http
o https
para el primer parámetro a loadDataWithBaseURL()
o, al menos, no file
, porque los errores). Y necesitará esa URL más tarde, así que manténgase en ella (por ejemplo, private static final String
valor de la private static final String
).
Paso # 2: Decida cuándo desea inicializar las comunicaciones desde el JavaScript en Java. Con addJavascriptInterface()
, esto está disponible inmediatamente. Sin embargo, usar WebMessagePort
no es casi tan agradable. En particular, no puede intentar inicializar las comunicaciones hasta que se cargue la página (por ejemplo, onPageFinished()
en un WebViewClient
).
Paso # 3: En el momento que desea inicializar esas comunicaciones, llame a createWebMessageChannel()
en el WebView
, para crear un WebMessagePort[]
. El 0 º elemento de esa matriz es el final de la tubería de comunicaciones, y puede llamar a setWebMessageCallback()
para poder responder a los mensajes que se le envíen desde JavaScript.
Paso # 4: Entregue el 1er elemento en ese WebMessagePort[]
al JavaScript envolviéndolo en un WebMessage
y llamando a postWebMessage()
en el WebView
. postWebMessage()
toma un Uri
como el segundo parámetro, y este Uri
debe derivarse de la misma URL que usó en el Paso # 1 como la URL base para loadDataWithBaseURL()
.
@TargetApi(Build.VERSION_CODES.M) private void initPort() { final WebMessagePort[] channel=wv.createWebMessageChannel(); port=channel[0]; port.setWebMessageCallback(new WebMessagePort.WebMessageCallback() { @Override public void onMessage(WebMessagePort port, WebMessage message) { postLux(); } }); wv.postWebMessage(new WebMessage("", new WebMessagePort[]{channel[1]}), Uri.parse(THIS_IS_STUPID)); }
(Donde wv
es el WebView
y THIS_IS_STUPID
es la URL usada con loadDataWithBaseURL()
)
Paso # 5: Su JavaScript puede asignar una función al evento onmessage
global, que se llamará cuando se llama a postWebMessage()
. El elemento 0 de la matriz de ports
que se obtiene en el evento será el final de JavaScript de la canalización de comunicaciones y se puede rellenar en una variable en alguna parte. Si lo desea, puede asignar una función a onmessage
para ese puerto, si el código Java usará WebMessagePort
para enviar datos futuros.
Paso 6: Cuando desee enviar un mensaje desde JavaScript a Java, llame a postMessage()
en el puerto del paso 5 y ese mensaje se enviará a la devolución de llamada que registró con setWebMessageCallback()
en el paso # 3.
var port; function pull() { port.postMessage("ping"); } onmessage = function (e) { port = e.ports[0]; port.onmessage = function (f) { parse(e.data); } }
Esta aplicación de ejemplo demuestra la técnica. Tiene un WebView
que muestra el nivel de luz actual basado en el sensor de luz ambiental. Los datos de los sensores se introducen en el WebView
ya sea en base al impulso (a medida que cambia el sensor) o bien en forma de tracción (el usuario teclea la etiqueta "Nivel de luz" en la página Web). Esta aplicación utiliza WebMessagePort
para estos dispositivos en Android 6.0 o WebMessagePort
, aunque la opción de empuje está comentada para que pueda confirmar que el método de extracción está funcionando a través del puerto. Tendré una cobertura más detallada de la aplicación de ejemplo en una próxima edición de mi libro .
Hay una prueba para ello en CTS
// Create a message channel and make sure it can be used for data transfer to/from js. public void testMessageChannel() throws Throwable { if (!NullWebViewUtils.isWebViewAvailable()) { return; } loadPage(CHANNEL_MESSAGE); final WebMessagePort[] channel = mOnUiThread.createWebMessageChannel(); WebMessage message = new WebMessage(WEBVIEW_MESSAGE, new WebMessagePort[]{channel[1]}); mOnUiThread.postWebMessage(message, Uri.parse(BASE_URI)); final int messageCount = 3; final CountDownLatch latch = new CountDownLatch(messageCount); runTestOnUiThread(new Runnable() { @Override public void run() { for (int i = 0; i < messageCount; i++) { channel[0].postMessage(new WebMessage(WEBVIEW_MESSAGE + i)); } channel[0].setWebMessageCallback(new WebMessagePort.WebMessageCallback() { @Override public void onMessage(WebMessagePort port, WebMessage message) { int i = messageCount - (int)latch.getCount(); assertEquals(WEBVIEW_MESSAGE + i + i, message.getData()); latch.countDown(); } }); } }); // Wait for all the responses to arrive. boolean ignore = latch.await(TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS); }
Archivo: cts/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java
. Al menos un punto de partida.
@CommonsWare He intentado su solución y funcionó para mí. Sólo una pequeña adición. También puede usar loadUrl()
al establecer el argumento Uri
en Uri.EMPTY
. Trabajo en Nexus 7 (MOB30J).
getWebView().postWebMessage(new WebMessage("MESSAGE", new WebMessagePort[]{ channel[1] }), Uri.EMPTY);