¿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.

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?

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); 
  • ¿Cómo resaltar el texto permanentemente en la vista web de Android?
  • ¿Es posible acceder a la caché WebView?
  • Android singleTap / OnClick en WebView
  • ¿Cómo evitar que una vista de desplazamiento se desplace a una vista web después de cargar los datos?
  • El menú desplegable de visualización web de Android no funciona en dispositivos Android 4.x
  • Pase el token de acceso de Facebook al cuadro de comentarios
  • Cómo detener la ejecución de JavaScript en Android Webview
  • ¿Hay alguna manera de interceptar JavaScript activado URL en webview?
  • La página Web Gujarati no se muestra perfecta en la vista Web
  • Vista web de Android lanza navegador cuando llama a loadurl
  • OnPageStart se llama muchas veces y onPageFinished no se llama para una sola página
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.