我试图浏览代码以了解addJavascriptInterface()
上WebView
的实施方式,但它潜入了native
代码,这基本上削弱了我理解正在发生的事情的能力
具体来说,我试图确定JNI(?)是否意味着addJavascriptInterface()
安排回调为Java代码依赖于getClass()
作为反射策略的一部分,以映射方法引用Java中实现的JavaScript源代码。我认为它必须和maybe I am searching in the wrong place,但我没有看到它。
有人能指出使用注入的Java对象的代码,那么我们可以看看它是如何实现的?
谢谢!
更新
澄清一下,我的意思是对传递给getClass()
的对象使用addJavascriptInterface()
。
答案 0 :(得分:5)
我认为您之后的代码可以在external/webkit/Source/WebCore/bridge/jni/
中找到。那里有两个主要的子目录,jsc
和v8
代表Android使用的两个Javascript引擎。由于V8是最近使用的引擎,并且有一段时间,我们会坚持使用它。
我假设您能够成功跟踪代码的Java端以从WebView.addJavascriptInterface()
转到BrowserFrame.nativeAddJavaScriptInterface()
,我会留下这些详细信息。原始端由AddJavaScriptInterface()
中的external/webkit/Source/WebKit/android/jni/WebCoreFrameBridge.cpp
选取,其中应用程序传入的Java对象最终绑定到带有bindToWindowObject()
的WebKit框架。
我试图确定JNI是否意味着addJavascriptInterface()安排回调为Java代码依赖于getClass()作为反射策略的一部分
简短的回答是肯定的。他们在传统的JNI代码中使用了很多包装器,但是如果你查看它们内部的JNIEnv
上的访问器就会出现反射。他们在V8中创建的包装是:
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
回到WebCoreFrameBridge.cpp
,在该对象传入的应用程序绑定之前,最初通过JNI传递给本机代码的jobject
被包装在JavaInstance
类中,然后转换到NPObject
,这是绑定到WebKit的最终对象。 V8 NPObject的源代码是:
external/webkit/Source/WebCore/bridge/jni/v8/JavaNPObjectV8.cpp
我们可以在NPObject
实现中看到,调用总是提取JavaInstance
并在那里调用方法。如果您查看JavaNPObjectHasMethod()
或JavaNPObjectInvoke
等示例,您会注意到以下行经常出现:
instance->getClass()->methodsNamed(name)
这将返回他们已创建的JavaClass
包装器,但是如果您查看JavaClassJobjectV8
构造函数和相关方法,您将看到那些熟悉的Java对象反射调用使用JNIEnv
(包括实际 JNI getClass()
致电Dalvik)。
因此,当绑定的WebKit框架调用方法时,它会找到关联的NPObject
,它会提取其JavaInstance
包装器,后者又使用JNI反射来访问Java方法。这里的监管链有点难以理解,所以让我知道已经提供的东西是否足以回答你的问题。
答案 1 :(得分:2)
这是我得到的:
WebView wv = ...;
wv.addJavascriptInterface(object, name);
这是:
public void addJavascriptInterface(Object object, String name) {
checkThread();
mProvider.addJavascriptInterface(object, name);
}
mProvider
是WebViewProvider
类型的接口,因为它在WebView
类中声明:
//-------------------------------------------------------------------------
// Private internal stuff
//-------------------------------------------------------------------------
private WebViewProvider mProvider;
我可以看到实例化它的唯一方法是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()
实现为:
private static synchronized WebViewFactoryProvider getFactory() {
return WebViewFactory.getProvider();
}
getProvider()
实现为:
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;
}
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;
}
这里是它使用Reflection的地方。如果在实例化自定义类期间发生异常,则将使用WebViewClassic.Factory()
。以下是它的实现方式:
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);
}
}
现在返回mProvider = getFactory().createWebView(this, new PrivateAccess());
,其中getFactory()
是自定义类(通过反射)或WebViewClassic.Factory
。
WebViewClassic.Factory#createWebView()
会返回WebViewClassic
,这是mProvider
类型的子类型。
WebViewClassic#addJavascriptInterface
实现为:
/**
* 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);
}
我想,这就是你要找的东西:)
答案 2 :(得分:0)
这更多是评论而非答案,但我无法在评论中添加堆栈跟踪。所以这就是:
在服务器作为JavaScript接口实现的对象中设置断点时,这是我得到的示例堆栈跟踪:
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)
它以Java开头(Thread.run
- &gt; handleMessage
)。然后它消失在Native代码(nativeServiceFuncPtrQueue
)中,它再次以Java(nativeServiceFuncPtrQueue
- &gt; JSReady
)出现。
此堆栈来自运行4.3的Nexus 10.
Native Layer中正在发生一些事情,它将执行从对nativeServiceFuncPtrQueue的调用直接转移到Java中的JavaScriptInterface实例的Java方法。
现在,JavaScriptInterface需要注释它发布到JavaScript的每个方法(@JavaScriptInterface
方法注释)。也许这会生成一些JNI桥接器,从Native调用到Java。
我想知道在不需要@JavaScriptInterface
注释的旧设备上,这种堆栈跟踪会是什么样子。
答案 3 :(得分:0)
来自Understanding Android's webview addjavascriptinterface:“方法WebView.addJavascriptInterface将消息发送到WebViewCore实例:
mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE,arg);在WebViewCore.java中有一堆称为sendMessage的重载方法,但我们并不需要知道究竟是哪个被调用,因为它们几乎完全相同。甚至有一个很好的评论给我们一个提示,我们在正确的地方!所有这些都委托给一个内部类的EventHub实例。这个方法结果是同步的,并且正在向Handler的一个实例发送消息,这很好地表明这可能在另一个线程中运行,但为了完整起见,让我们找出来!
Handler在EventHub.transferMessages中实例化,该调用是从WebViewCore.initialize调用的。这里还有一些跳,但最终我发现这是从WebCoreThread(Runnable的子类)中运行调用的,它在这里实例化了一个新的Thread。“在这里实例化了一个新的Thread。” / p>
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));
}
}
}
换句话说,在我看来,这可能是一连串的电话:
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 }
返回此Object的运行时类。返回的Class对象是由所表示的类的静态同步方法锁定的对象。实际结果类型是Class,其中| X |是调用getClass的表达式的静态类型的擦除。例如,此代码片段中不需要强制转换:
Number n = 0;
Class<? extends Number> c = n.getClass();
返回:表示此对象的运行时类的Class对象。另请参阅:Java语言规范,第三版(15.8.2类文字)
64
65 public final native Class<?> getClass();
从Dalvik的角度来看,我认为你只是通过JNIHelp.c这样的findClass注册一个JNI回调:
/*
* 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;
}
总之,我的想法来自Native Libraries:
//Get jclass with env->FindClass
所以也许可以使用FindClass代替getClass ......