2012-06-09 12 views
21

Sé que para interactuar de JavaScript a Java, debe inyectar un objeto Java utilizando el método addjavascriptInterface en webview.Comprensión de la vista web de Android addjavascriptinterface

Aquí está el problema que estoy enfrentando.

  1. registro un objeto Java usando addJavascriptInterface método que esté disponible en mis JS.

  2. me inyecto pocos JS en la vista web utilizando webview.loadURL("javascript:XXX");

  3. envío un caso JS cuando he terminado con la inyección de la JS.

El problema es que si inmediatamente después del paso 1, si ejecuta la siguiente Javascript:

mWebView.loadUrl("javascript:if(window.myobject) console.log('myobject found---------'); else {console.log('myobject not found----');}"); 

consigo "myObject no encontrado" en el diario de mi consola.

Quiero saber si hay algún tiempo antes de poder acceder a mi objeto y, de ser así, ¿cómo puedo saber cuánto tiempo debo esperar para llamar a mi objeto?

Respuesta

44

Quiero saber que si hay algo de tiempo antes de que pueda acceder a mi objeto

Sí, creo que hay un retraso, porque WebView.addJavascriptInterface se ejecutará en el subproceso de trabajo interno de la vista Web. Tal vez haya pensado en esto y se haya dado cuenta de que WebView debe mantener al menos un hilo de trabajo para hacer IO de red asíncrona. Quizás también haya notado estos hilos en DDMS al usar un WebView.

Resulta que también utiliza un hilo para trabajar con otros métodos públicos. Realmente deseo que los documentos de Google lo aclaren. Pero espero poder ayudarte y mostrarte cómo intenté confirmar esto por mí mismo.

Sígueme cuando eche un vistazo a la fuente de WebView. Es razonablemente legible, incluso si no puede seguir exactamente lo que está sucediendo, es posible rastrear a través de la respuesta algunas preguntas con respecto a los hilos.

Puede descargar la fuente del marco de Android a través de la herramienta del administrador de SDK, pero también se refleja en Github, así que eso es lo que he vinculado aquí. Adiviné y elegí una etiqueta que está cerca de alguna versión de ICS. No es difícil encontrar . Acabo de buscar en Google "WebView.java site: github.com/android".

El método WebView.addJavascriptInterface envía un mensaje a una instancia de WebViewCore:

mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg); 

En WebViewCore.java hay un montón de métodos sobrecargados llamados sendMessage, pero que en realidad no necesitamos saber exactamente lo que se está llamando, ya que hacen más o menos lo mismo. ¡Incluso hay un buen comentario para darnos una pista de que estamos en el lugar correcto! Todos ellos están delegando a una instancia de EventHub que es una clase interna. This method resulta estar sincronizado, y está enviando un mensaje a una instancia de Handler, lo cual es una buena indicación de que esto probablemente se está ejecutando en otro hilo, pero, para más información, ¡descubramos!

Eso Handler se instancia en EventHub.transferMessages que se llama desde WebViewCore.initialize. Hay algunos saltos más aquí, pero finalmente descubrí que esto se llama desde run en WebCoreThread (subclase de Runnable), que se crea una instancia junto con un nuevo Thread derecho here.

¡Qué aventura! Entonces, aunque realmente no puedo decir con certeza qué está pasando con todas estas partes móviles, estoy bastante seguro de que este método no es síncrono y envía un mensaje al hilo de trabajo de WebView. ¡Espero que tenga sentido!

si es así, ¿cómo puedo saber cuánto tiempo debo esperar para llamar a mi objeto?

Lamentablemente, no sé la respuesta a esto. Estaba investigando este problema exacto y encontré esta pregunta en StackOverflow durante mi búsqueda en Google. Creo que tiene las siguientes opciones, algunas de las cuales son más agradables o más fáciles que otras:

1) Solo Thread.sleep durante 100 ms o algo entre addJavascriptInterface y loadUrl("javascript:..."). Blech, no me gusta esto, pero es potencialmente el más fácil.

2) Otra posibilidad es que pueda llamar al WebView.loadUrl con un fragmento de JavaScript que prueba específicamente si la interfaz está configurada, y capta el error de referencia que se lanza si aún no está configurado. Sin embargo, como habrás adivinado, ¡esto implica agregar una interfaz de JavaScript a WebView!

3) Llame WebView.setWebChromeClient en su lugar, y capture JavaScript alert() o console.log en su lugar. De mis experimentos, este método es síncrono, por lo que no hay demora. (He confirmado esto en la fuente, pero dejaré los detalles como un ejercicio para el lector). Probablemente deberías encontrar una secuencia especial para llamar al alert y verificarla dentro de onJsAlert, por lo que no solo estás atrapando todos alert() s.

Disculpa la duración de esta respuesta, espero que ayude. ¡Buena suerte!

+0

Buena respuesta elaborada con referencias apropiadas. –

+0

genial. buena respuesta. Pero revisar las alertas y los mensajes de la consola son tan malos como hacer Thread.sleep() :) – Akshat

+0

Oye, todos advertí que las opciones son subóptimas: P – spacemanaki

1

Asegúrese de que los objetos de Javascript declarados en su HTML/Javascript a los que necesita acceder desde Java se declaren globales, de lo contrario lo más probable es que se recopilen. Tengo el código que hace esto (en el que Android es mi interfaz añadido con addJavascriptInterface):

método
<script> 
    var cb = function(location) { 
    alert('location is ' + location); 
    } 
    Android.getLocation('cb'); 
</script> 

El getLocation invoca LocationManager.requestSingleUpdate de Android, que invoca la devolución de llamada cuando se dispara el LocationListener.

Sin la "var" me parece que en el momento en que la búsqueda de la ubicación invoca la devolución de llamada la función de devolución de llamada ha sido recolectada.

0

(copiada de mi respuesta en a similar question)

Me he tomado la ejecución del Sr. S como la piedra angular de mi arreglo y mejorado enormemente y de Jason Shah.

Hay demasiado código para poner en este comentario, solo lo vincularé.

puntos clave son:

  • artículo se refiere a todas las versiones de Gingerbread (2.3.x)
  • Las llamadas de JS para Android ahora son síncronas
  • Ya no tienen t o trazar métodos de interfaz manualmente
  • posibilidad fijo de separadores de cadena para romper el código
  • mucho más fácil cambiar JS firma y nombres de interfaz
Cuestiones relacionadas