¿AddJavascriptInterface () depende de getClass ()?

He intentado rastrear a través del código para ver cómo addJavascriptInterface() en WebView se implementa, pero se sumerge en código native , que básicamente paraliza mi capacidad de grok lo que está pasando.

Específicamente, estoy tratando de determinar si el JNI (?) Significa que addJavascriptInterface() organiza para llamar de nuevo a código de Java se basa en getClass() como parte de una estrategia de reflexión, para asignar referencias de método en fuente JavaScript a las implementaciones en Java . Supongo que tiene que hacerlo, y tal vez estoy buscando en el lugar equivocado , pero no lo veo.

¿Puede alguien señalarme el código donde se utilizan los objetos Java inyectados, para que podamos ver cómo se implementa?

¡Gracias!


ACTUALIZAR

Para aclarar, me refiero a usar getClass() en el objeto pasado a addJavascriptInterface() .

El código que creo que buscas se encuentra en external/webkit/Source/WebCore/bridge/jni/ . Hay dos subdirectorios principales, jsc y v8 representan los dos motores Javascript que Android ha utilizado. Puesto que V8 es el motor que se ha utilizado más recientemente y durante algún tiempo, nos quedaremos con eso.

Estoy asumiendo que fueron capaces de rastrear con éxito el lado Java del código para llegar desde WebView.addJavascriptInterface() hasta BrowserFrame.nativeAddJavaScriptInterface() , dejaré esos detalles. El lado nativo es recogido por AddJavaScriptInterface() en external/webkit/Source/WebKit/android/jni/WebCoreFrameBridge.cpp , donde el objeto Java pasado por la aplicación finalmente está enlazado al marco WebKit con bindToWindowObject() .

Estoy tratando de determinar si el JNI significa que addJavascriptInterface () organiza para llamar de nuevo a código de Java se basa en getClass () como parte de una estrategia de reflexión

La respuesta corta es sí. Ellos usan una gran cantidad de envolturas alrededor del código JNI tradicional, pero si usted mira dentro de ellos los JNIEnv en el JNIEnv para hacer la reflexión están presentes. Los wrappers que han creado en V8 son:

external/webkit/Source/WebCore/bridge/jni/v8/JavaInstanceJobjectV8.cpp external/webkit/Source/WebCore/bridge/jni/v8/JavaClassJobjectV8.cpp external/webkit/Source/WebCore/bridge/jni/v8/JavaMethodJobjectV8.cpp

Volviendo a WebCoreFrameBridge.cpp , antes de que el objeto de la aplicación pasada está vinculado, el jobject originalmente entregado en el código nativo a través de JNI se envuelve en una clase JavaInstance , y luego se convierte en un NPObject , que es el objeto final vinculado a WebKit. La fuente del objeto NPO de V8 está en: external/webkit/Source/WebCore/bridge/jni/v8/JavaNPObjectV8.cpp

Podemos ver en la implementación de NPObject que las llamadas siempre extraen los JavaInstance back out y call allí. Si observa ejemplos como JavaNPObjectHasMethod() o JavaNPObjectInvoke , notará que la siguiente línea aparece con frecuencia:

instance->getClass()->methodsNamed(name)

Esto devuelve el wrapper de JavaClass que han creado, pero si observa el constructor JavaClassJobjectV8 y los métodos asociados, verá aquellas llamadas de reflexión familiares al objeto Java usando JNIEnv (incluida la llamada real de getClass() a Dalvik).

Así, cuando un método es llamado por el marco WebKit enlazado, encuentra el NPObject asociado, que extrae su wrapper JavaInstance , que a su vez utiliza la reflexión JNI para obtener acceso a los métodos Java. La cadena de custodia aquí es un poco más difícil de seguir, así que hágamelo saber si lo que ya se proporciona es suficiente para responder a sus preguntas.

Esto es lo que tengo:

 WebView wv = ...; wv.addJavascriptInterface(object, name); 

Esto va a:

 public void addJavascriptInterface(Object object, String name) { checkThread(); mProvider.addJavascriptInterface(object, name); } 

mProvider es una interfaz del tipo WebViewProvider como se declara en la clase WebView :

 //------------------------------------------------------------------------- // Private internal stuff //------------------------------------------------------------------------- private WebViewProvider mProvider; 

El único método que puedo ver que instancia es ensureProviderCreated() :

 private void ensureProviderCreated() { checkThread(); if (mProvider == null) { // As this can get called during the base class constructor chain, pass the minimum // number of dependencies here; the rest are deferred to init(). mProvider = getFactory().createWebView(this, new PrivateAccess()); } } 

getFactory() se implementa como:

 private static synchronized WebViewFactoryProvider getFactory() { return WebViewFactory.getProvider(); } 

getProvider() se implementa como:

 static synchronized WebViewFactoryProvider getProvider() { // For now the main purpose of this function (and the factory abstraction) is to keep // us honest and minimize usage of WebViewClassic internals when binding the proxy. if (sProviderInstance != null) return sProviderInstance; sProviderInstance = getFactoryByName(DEFAULT_WEB_VIEW_FACTORY); if (sProviderInstance == null) { if (DEBUG) Log.v(LOGTAG, "Falling back to explicit linkage"); sProviderInstance = new WebViewClassic.Factory(); } return sProviderInstance; } 

getFactoryByName() se implementa como:

 private static WebViewFactoryProvider getFactoryByName(String providerName) { try { if (DEBUG) Log.v(LOGTAG, "attempt to load class " + providerName); Class<?> c = Class.forName(providerName); if (DEBUG) Log.v(LOGTAG, "instantiating factory"); return (WebViewFactoryProvider) c.newInstance(); } catch (ClassNotFoundException e) { Log.e(LOGTAG, "error loading " + providerName, e); } catch (IllegalAccessException e) { Log.e(LOGTAG, "error loading " + providerName, e); } catch (InstantiationException e) { Log.e(LOGTAG, "error loading " + providerName, e); } return null; } 

Y aquí es donde se utiliza la reflexión. Si se produce una excepción durante la instancia de la clase personalizada, se WebViewClassic.Factory() . Aquí es cómo se implementa:

 static class Factory implements WebViewFactoryProvider, WebViewFactoryProvider.Statics { @Override public String findAddress(String addr) { return WebViewClassic.findAddress(addr); } @Override public void setPlatformNotificationsEnabled(boolean enable) { if (enable) { WebViewClassic.enablePlatformNotifications(); } else { WebViewClassic.disablePlatformNotifications(); } } @Override public Statics getStatics() { return this; } @Override public WebViewProvider createWebView(WebView webView, WebView.PrivateAccess privateAccess) { return new WebViewClassic(webView, privateAccess); } @Override public GeolocationPermissions getGeolocationPermissions() { return GeolocationPermissionsClassic.getInstance(); } @Override public CookieManager getCookieManager() { return CookieManagerClassic.getInstance(); } @Override public WebIconDatabase getWebIconDatabase() { return WebIconDatabaseClassic.getInstance(); } @Override public WebStorage getWebStorage() { return WebStorageClassic.getInstance(); } @Override public WebViewDatabase getWebViewDatabase(Context context) { return WebViewDatabaseClassic.getInstance(context); } } 

Ahora vuelve a mProvider = getFactory().createWebView(this, new PrivateAccess()); Donde getFactory() es la clase personalizada (por reflexión) o WebViewClassic.Factory .

WebViewClassic.Factory#createWebView() devuelve WebViewClassic que es un subtipo del tipo mProvider .

WebViewClassic#addJavascriptInterface se implementa como:

 /** * See {@link WebView#addJavascriptInterface(Object, String)} */ @Override public void addJavascriptInterface(Object object, String name) { if (object == null) { return; } WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData(); arg.mObject = object; arg.mInterfaceName = name; mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg); } 

Creo que esto es lo que estás buscando 🙂

Esto es más un comentario que una respuesta, pero no puedo agregar un stacktrace en los comentarios. Así que aquí va:

Al establecer un punto de interrupción en un objeto que los servidores como una implementación de interfaz JavaScript, esto es una muestra de seguimiento de pila que obtengo:

 16> WebViewCoreThread@830034675584, prio=5, in group 'main', status: 'RUNNING' at com.mediaarc.player.books.model.pagesource.service.EPubPageSourceService$JS.JSReady(EPubPageSourceService.java:1752) at android.webkit.JWebCoreJavaBridge.nativeServiceFuncPtrQueue(JWebCoreJavaBridge.java:-1) at android.webkit.JWebCoreJavaBridge.nativeServiceFuncPtrQueue(JWebCoreJavaBridge.java:-1) at android.webkit.JWebCoreJavaBridge.handleMessage(JWebCoreJavaBridge.java:113) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:137) at android.webkit.WebViewCore$WebCoreThread.run(WebViewCore.java:814) at java.lang.Thread.run(Thread.java:841) 

Se inicia en Java ( Thread.run -> handleMessage ). Luego desaparece en el código nativo ( nativeServiceFuncPtrQueue ) y sale de nuevo en Java ( nativeServiceFuncPtrQueue -> JSReady ).

Esta pila es de un Nexus 10 corriendo 4.3.

Hay algo que sucede en la capa nativa que mueve la ejecución desde dentro de una llamada a nativeServiceFuncPtrQueue directamente al método Java de la instancia JavaScriptInterface en Java.

Hoy en día, JavaScriptInterface necesita anotar cada método que publica en JavaScript (anotación de método @JavaScriptInterface ). Tal vez esto genera algunos puentes JNI sobre la marcha llamando de Native a Java.

Me pregunto cómo se vería esta pila-rastro en un dispositivo más viejo donde las anotaciones de @JavaScriptInterface no eran necesarias.

From Understanding Android webview addjavascriptinterface : "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 realmente no necesitamos saber qué exactamente se está llamando, ya que hacen casi lo mismo. Incluso hay un comentario agradable para darnos una pista de que estamos en el lugar correcto! Todos ellos están delegando a una instancia de EventHub que es alguna clase interna. Este método resulta ser sincronizado, y está enviando un mensaje a una instancia de Handler, que es una buena indicación de que esto probablemente se está ejecutando en otro hilo, pero por razones de integridad, vamos a averiguarlo!

Ese controlador se instancia en EventHub.transferMessages que se llama desde WebViewCore.initialize. Hay algunos saltos más aquí, pero al final descubrí que esto se llama desde ejecutar en WebCoreThread (subclase de Runnable), que se instancia junto con un nuevo hilo aquí. "Instanciado junto con un nuevo hilo aquí".

  synchronized (WebViewCore.class) { if (sWebCoreHandler == null) { // Create a global thread and start it. Thread t = new Thread(new WebCoreThread()); t.setName(THREAD_NAME); t.start(); try { WebViewCore.class.wait(); } catch (InterruptedException e) { Log.e(LOGTAG, "Caught exception while waiting for thread " + "creation."); Log.e(LOGTAG, Log.getStackTraceString(e)); } } } 

En otras palabras, esta podría ser la cadena de llamadas en mi opinión:

Android.webkit.WebViewClassic

  4159 @Override 4160 public void More ...addJavascriptInterface(Object object, String name) { 4161 4162 if (object == null) { 4163 return; 4164 } 4165 WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData(); 4166 4167 arg.mObject = object; 4168 arg.mInterfaceName = name; 4169 4170 // starting with JELLY_BEAN_MR1, annotations are mandatory for enabling access to 4171 // methods that are accessible from JS. 4172 if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR1) { 4173 arg.mRequireAnnotation = true; 4174 } else { 4175 arg.mRequireAnnotation = false; 4176 } 4177 mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg); 4178 } 

Android.webkit.WebViewCore

  static class JSInterfaceData { 827 Object mObject; 828 String mInterfaceName; 829 boolean mRequireAnnotation; 830 } 

Java.lang.Object

  37 public class Object { 38 39 private static native void registerNatives(); 40 static { 41 registerNatives(); 42 } 

Devuelve la clase runtime de este Object. El objeto de clase devuelto es el objeto que está bloqueado por métodos estáticos sincronizados de la clase representada. El tipo de resultado real es Class donde | X | Es el borrado del tipo estático de la expresión en la que se llama getClass. Por ejemplo, no se requiere emisión en este fragmento de código:

  Number n = 0; Class<? extends Number> c = n.getClass(); 

Devuelve: El objeto Class que representa la clase runtime de este objeto. Véase también: La especificación de lenguaje Java, tercera edición (15.8.2 literales de clase)

  64 65 public final native Class<?> getClass(); 

Desde la perspectiva de Dalvik, creo que solo estás registrando una devolución de llamada JNI a través de findClass como esta desde JNIHelp.c :

  /* * Register native JNI-callable methods. * * "className" looks like "java/lang/String". */ int jniRegisterNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods) { jclass clazz; LOGV("Registering %s natives\n", className); clazz = (*env)->FindClass(env, className); if (clazz == NULL) { LOGE("Native registration unable to find class '%s', aborting\n", className); abort(); } if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) { LOGE("RegisterNatives failed for '%s', aborting\n", className); abort(); } (*env)->DeleteLocalRef(env, clazz); return 0; } 

En conclusión mi idea se deriva de bibliotecas nativas :

 //Get jclass with env->FindClass 

Así que quizás FindClass podría ser utilizado en lugar de getClass …

  • Entender el desmontaje del código Dalvik?
  • La depuración de Eclipse no funciona correctamente con el tiempo de ejecución de ART
  • Programación de juegos para Android - Heap issues
  • ¿Cuáles son las principales diferencias entre una VM de base de registro y una basada en una pila?
  • ¿Hay un trabajo alrededor para el error de Android "no poder resolver el método virtual java / beans / PropertyDescriptor"?
  • Carga modificada /system/framework/*.jar sin reiniciar
  • 64 bits android, aplicación de 32 bits con 32 bits de biblioteca nativa
  • Diferencias fundamentales entre el sistema operativo Linux y Mobile OS (android)
  • Funcionamiento de la colada en diversos niveles al tirar abajo
  • Cómo y qué convierte el bytecode de java en el archivo dex Android
  • ¿Qué causa exactamente un error de "spin on suspend" en Android?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.