Android WebView中JAVA与JS之间的传递(一)
2015-10-31 13:58
1856 查看
本过程分析基于android4.3(webkit NPAPI),js引擎为v8。不同Android版本的区别
WebView包含两个部分,一部分是上层的Java代码(/frameworks/base/core/java/android/webkit),包括若干Java类,用于对外提供接口;另一部分是下层的C++代码(/external/webkit),包括两个so库(libwebcore.so和libchromium_net.so),用于网页的解析和渲染。两个部分之间通过JNI进行交互。
这段代码指定了所使用的动态链接库,即libwebcore.so和libchromium_net.so。
该函数中,从java环境中传入的jobject javascriptObj先转化为WeakJavaInstance addedObject(WeakJavaInstance由JavaInstanceJobject派生而来)对象,再由NPObject* JavaInstanceToNPObject(JavaInstance* instance)函数转为NPObject npObject(之后分析),最后使用void ScriptController::bindToWindowObject(Frame* frame, const String& key, NPObject* object)方法将npObject绑定至javascript上下文中。
注:
v8引擎沿用了一部分javascriptCore的代码。从上面源代码的最后一段注释可以了解到这个有趣的现象。
NPObject是NPAPI中重要的一个结构体,js中的各种变量等都是包装为这个结构体由javascript引擎直接对其操作。V8使用的v8Object是对其的一个扩充,其中包含了指向NPObject的指针(为了兼容)。
NPObject由一个指向NPClass的指针和引用计数组成。
除了structVersion外都是指向这些功能函数的指针。
需要注意的是将JavaInstance转为NPObject时对这个NPObject的NPClass做了一个针对java的特殊的初始化,初始化为JavaNPObjectClass。
其中JavaNPObjectInvoke指向的函数用于在JS中调用JAVA方法时将所需信息传递回JAVA环境,并调用反射机制执行。JavaNPObjectClass也表明了JS能对该JAVA对象执行哪些操作。
这个函数会将传入的NPAPI的结构体经过转换后,调用JAVA中的反射机制执行并获取返回结果,也就是在NPAPI和JAVA之间起了桥梁作用。从JS -> JAVA方向进行分析时,最后也会回到这个函数。
Line130~143为将反射的方法与方法列表进行比较(反射机制相关)。
最后分析一下 pFrame->script()->bindToWindowObject(pFrame, name, npObject);
在该函数中首先将之前传过来的npObject(此处为object)转变为V8引擎所使用的V8Object(该结构体声明在:/external/webkit/Source/WebCore/bindings/v8/NPV8Object.h)。最后将该V8Object的句柄附加到globle中,即可在js上下文中调用。
createV8ObjectForNPObject(…)方法位于/external/webkit/Source/WebCore/bindings/v8/V8NPObject.cpp
这个函数用于将JS中获取的JAVA对象和JS格式的参数(JAVA反射机制将使用这个对象和参数)转换为NPAPI中通用的结构体,并调用对应的Invoke函数,在Invoke函数中进一步处理后交由JAVA环境处理,也就是在JS和NPAPI中起到了一个桥梁作用。
Line106~107为参数打包,Line113~131为根据标识码调用对应的Invoke函数。
参数打包使用的函数在 /external/webkit/Source/WebCore/bindings/v8/V8NPUtils.cpp 中
Invoke函数在 /external/webkit/Source/WebCore/bridge/jni/v8/JavaNPObjectV8.cpp 中,也就是上面提到的bool JavaNPObjectInvoke(NPObject* obj, NPIdentifier identifier, const NPVariant* args, uint32_t argCount, NPVariant* result)
未完待续…
WebView包含两个部分,一部分是上层的Java代码(/frameworks/base/core/java/android/webkit),包括若干Java类,用于对外提供接口;另一部分是下层的C++代码(/external/webkit),包括两个so库(libwebcore.so和libchromium_net.so),用于网页的解析和渲染。两个部分之间通过JNI进行交互。
// /framework/base/core/java/android/webkit/JniUtil.java 30 static { 31 System.loadLibrary("webcore"); 32 System.loadLibrary("chromium_net"); 33 }
这段代码指定了所使用的动态链接库,即libwebcore.so和libchromium_net.so。
JAVA -> JS
Android 源代码-交叉引用// /external/webkit/Source/WebKit/android/jni/WebCoreFrameBridge.cpp 1573 static void AddJavascriptInterface(JNIEnv *env, jobject obj, jint nativeFramePointer, 1574 jobject javascriptObj, jstring interfaceName, jboolean requireAnnotation) 1575 { 1576 WebCore::Frame* pFrame = 0; 1577 if (nativeFramePointer == 0) 1578 pFrame = GET_NATIVE_FRAME(env, obj); 1579 else 1580 pFrame = (WebCore::Frame*)nativeFramePointer; 1581 ALOG_ASSERT(pFrame, "nativeAddJavascriptInterface must take a valid frame pointer!"); 1582 1583 JavaVM* vm; 1584 env->GetJavaVM(&vm); 1585 ALOGV("::WebCore:: addJSInterface: %p", pFrame); 1586 1587 if (pFrame) { 1588 RefPtr<JavaInstance> addedObject = WeakJavaInstance::create(javascriptObj, 1589 requireAnnotation); 1590 const char* name = getCharactersFromJStringInEnv(env, interfaceName); 1591 // Pass ownership of the added object to bindToWindowObject. 1592 NPObject* npObject = JavaInstanceToNPObject(addedObject.get()); 1593 pFrame->script()->bindToWindowObject(pFrame, name, npObject); 1594 // bindToWindowObject calls NPN_RetainObject on the 1595 // returned one (see createV8ObjectForNPObject in V8NPObject.cpp). 1596 // bindToWindowObject also increases obj's ref count and decreases 1597 // the ref count when the object is not reachable from JavaScript 1598 // side. Code here must release the reference count increased by 1599 // bindToWindowObject. 1600 1601 // Note that while this function is declared in WebCore/bridge/npruntime.h, for V8 builds 1602 // we use WebCore/bindings/v8/npruntime.cpp (rather than 1603 // WebCore/bridge/npruntime.cpp), so the function is implemented there. 1604 // TODO: Combine the two versions of these NPAPI files. 1605 NPN_ReleaseObject(npObject); 1606 releaseCharactersForJString(interfaceName, name); 1607 } 1608 }
该函数中,从java环境中传入的jobject javascriptObj先转化为WeakJavaInstance addedObject(WeakJavaInstance由JavaInstanceJobject派生而来)对象,再由NPObject* JavaInstanceToNPObject(JavaInstance* instance)函数转为NPObject npObject(之后分析),最后使用void ScriptController::bindToWindowObject(Frame* frame, const String& key, NPObject* object)方法将npObject绑定至javascript上下文中。
注:
v8引擎沿用了一部分javascriptCore的代码。从上面源代码的最后一段注释可以了解到这个有趣的现象。
NPObject是NPAPI中重要的一个结构体,js中的各种变量等都是包装为这个结构体由javascript引擎直接对其操作。V8使用的v8Object是对其的一个扩充,其中包含了指向NPObject的指针(为了兼容)。
// /external/webkit/Source/WebCore/bridge/npruntime.h 323 struct NPObject { 324 NPClass *_class; 325 uint32_t referenceCount; 326 /* 327 * Additional space may be allocated here by types of NPObjects 328 */ 329 };
NPObject由一个指向NPClass的指针和引用计数组成。
// /external/webkit/Source/WebCore/bridge/npruntime.h 295 struct NPClass 296 { 297 uint32_t structVersion; 298 NPAllocateFunctionPtr allocate; 299 NPDeallocateFunctionPtr deallocate; 300 NPInvalidateFunctionPtr invalidate; 301 NPHasMethodFunctionPtr hasMethod; 302 NPInvokeFunctionPtr invoke; 303 NPInvokeDefaultFunctionPtr invokeDefault; 304 NPHasPropertyFunctionPtr hasProperty; 305 NPGetPropertyFunctionPtr getProperty; 306 NPSetPropertyFunctionPtr setProperty; 307 NPRemovePropertyFunctionPtr removeProperty; 308 NPEnumerationFunctionPtr enumerate; 309 NPConstructFunctionPtr construct; 310 };
除了structVersion外都是指向这些功能函数的指针。
需要注意的是将JavaInstance转为NPObject时对这个NPObject的NPClass做了一个针对java的特殊的初始化,初始化为JavaNPObjectClass。
// /external/webkit/Source/WebCore/bridge/jni/v8/JavaNPObjectV8.cpp 76 NPObject* JavaInstanceToNPObject(JavaInstance* instance) 77 { 78 JavaNPObject* object = reinterpret_cast<JavaNPObject*>(_NPN_CreateObject(0,&JavaNPObjectClass)); 79 object->m_instance = instance; 80 return reinterpret_cast<NPObject*>(object); 81 } ... 60 static NPClass JavaNPObjectClass = { 61 NP_CLASS_STRUCT_VERSION, 62 AllocJavaNPObject, // allocate, 63 FreeJavaNPObject, // free, 64 0, // invalidate 65 JavaNPObjectHasMethod, 66 JavaNPObjectInvoke, 67 0, // invokeDefault, 68 JavaNPObjectHasProperty, 69 JavaNPObjectGetProperty, 70 0, // setProperty 71 0, // removeProperty 72 0, // enumerate 73 0 // construct 74 };
其中JavaNPObjectInvoke指向的函数用于在JS中调用JAVA方法时将所需信息传递回JAVA环境,并调用反射机制执行。JavaNPObjectClass也表明了JS能对该JAVA对象执行哪些操作。
// /external/webkit/Source/WebCore/bridge/jni/v8/JavaNPObjectV8.cpp 110 bool JavaNPObjectInvoke(NPObject* obj, NPIdentifier identifier, const NPVariant* args, uint32_t argCount, NPVariant* result) 111 { 112 JavaInstance* instance = ExtractJavaInstance(obj); 113 if (!instance) 114 return false; 115 NPUTF8* name = _NPN_UTF8FromIdentifier(identifier); 116 if (!name) 117 return false; 118 119 instance->begin(); 120 121 MethodList methodList = instance->getClass()->methodsNamed(name); 122 // TODO: use NPN_MemFree 123 free(name); 124 125 // Try to find a good match for the overloaded method. The 126 // fundamental problem is that JavaScript doesn't have the 127 // notion of method overloading and Java does. We could 128 // get a bit more sophisticated and attempt to do some 129 // type checking as well as checking the number of parameters. 130 size_t numMethods = methodList.size(); 131 JavaMethod* aMethod; 132 JavaMethod* jMethod = 0; 133 for (size_t methodIndex = 0; methodIndex < numMethods; methodIndex++) { 134 aMethod = methodList[methodIndex]; 135 if (aMethod->numParameters() == static_cast<int>(argCount)) { 136 jMethod = aMethod; 137 break; 138 } 139 } 140 if (!jMethod) { 141 instance->end(); 142 return false; 143 } 144 145 JavaValue* jArgs = new JavaValue[argCount]; 146 for (unsigned int i = 0; i < argCount; i++) 147 jArgs[i] = convertNPVariantToJavaValue(args[i], jMethod->parameterAt(i)); 148 149 // ANDROID 150 bool exceptionOccurred; 151 JavaValue jResult = instance->invokeMethod(jMethod, jArgs, exceptionOccurred); 152 instance->end(); 153 delete[] jArgs; 154 155 if (exceptionOccurred) 156 return false; 157 // END ANDROID 158 159 VOID_TO_NPVARIANT(*result); 160 convertJavaValueToNPVariant(jResult, result); 161 return true; 162 }
这个函数会将传入的NPAPI的结构体经过转换后,调用JAVA中的反射机制执行并获取返回结果,也就是在NPAPI和JAVA之间起了桥梁作用。从JS -> JAVA方向进行分析时,最后也会回到这个函数。
Line130~143为将反射的方法与方法列表进行比较(反射机制相关)。
最后分析一下 pFrame->script()->bindToWindowObject(pFrame, name, npObject);
// /external/webkit/Source/WebCore/bindings/v8/ScriptController.cpp 262 // Create a V8 object with an interceptor of NPObjectPropertyGetter. 263 void ScriptController::bindToWindowObject(Frame* frame, const String& key, NPObject* object) 264 { 265 v8::HandleScope handleScope; 266 267 v8::Handle<v8::Context> v8Context = V8Proxy::mainWorldContext(frame); 268 if (v8Context.IsEmpty()) 269 return; 270 271 v8::Context::Scope scope(v8Context); 272 273 v8::Handle<v8::Object> value = createV8ObjectForNPObject(object, 0); 274 275 // Attach to the global object. 276 v8::Handle<v8::Object> global = v8Context->Global(); 277 global->Set(v8String(key), value); 278 }
在该函数中首先将之前传过来的npObject(此处为object)转变为V8引擎所使用的V8Object(该结构体声明在:/external/webkit/Source/WebCore/bindings/v8/NPV8Object.h)。最后将该V8Object的句柄附加到globle中,即可在js上下文中调用。
createV8ObjectForNPObject(…)方法位于/external/webkit/Source/WebCore/bindings/v8/V8NPObject.cpp
JS -> JAVA
// /external/webkit/Source/WebCore/bindings/v8/V8NPObject.cpp 69 static v8::Handle<v8::Value> npObjectInvokeImpl(const v8::Arguments& args, InvokeFunctionType functionId) 70 { 71 NPObject* npObject; 72 73 // These three types are subtypes of HTMLPlugInElement. 74 if (V8HTMLAppletElement::HasInstance(args.Holder()) || V8HTMLEmbedElement::HasInstance(args.Holder()) 75 || V8HTMLObjectElement::HasInstance(args.Holder())) { 76 // The holder object is a subtype of HTMLPlugInElement. 77 HTMLPlugInElement* element; 78 if (V8HTMLAppletElement::HasInstance(args.Holder())) 79 element = V8HTMLAppletElement::toNative(args.Holder()); 80 else if (V8HTMLEmbedElement::HasInstance(args.Holder())) 81 element = V8HTMLEmbedElement::toNative(args.Holder()); 82 else 83 element = V8HTMLObjectElement::toNative(args.Holder()); 84 ScriptInstance scriptInstance = element->getInstance(); 85 if (scriptInstance) 86 npObject = v8ObjectToNPObject(scriptInstance->instance()); 87 else 88 npObject = 0; 89 } else { 90 // The holder object is not a subtype of HTMLPlugInElement, it must be an NPObject which has three 91 // internal fields. 92 if (args.Holder()->InternalFieldCount() != npObjectInternalFieldCount) 93 return throwError("NPMethod called on non-NPObject", V8Proxy::ReferenceError); 94 95 npObject = v8ObjectToNPObject(args.Holder()); 96 } 97 98 // Verify that our wrapper wasn't using a NPObject which has already been deleted. 99 if (!npObject || !_NPN_IsAlive(npObject)) 100 return throwError("NPObject deleted", V8Proxy::ReferenceError); 101 102 // Wrap up parameters. 103 int numArgs = args.Length(); 104 OwnArrayPtr<NPVariant> npArgs = adoptArrayPtr(new NPVariant[numArgs]); 105 106 for (int i = 0; i < numArgs; i++) 107 convertV8ObjectToNPVariant(args[i], npObject, &npArgs[i]); 108 109 NPVariant result; 110 VOID_TO_NPVARIANT(result); 111 112 bool retval = true; 113 switch (functionId) { 114 case InvokeMethod: 115 if (npObject->_class->invoke) { 116 v8::Handle<v8::String> functionName(v8::String::Cast(*args.Data())); 117 NPIdentifier identifier = getStringIdentifier(functionName); 118 retval = npObject->_class->invoke(npObject, identifier, npArgs.get(), numArgs, &result); 119 } 120 break; 121 case InvokeConstruct: 122 if (npObject->_class->construct) 123 retval = npObject->_class->construct(npObject, npArgs.get(), numArgs, &result); 124 break; 125 case InvokeDefault: 126 if (npObject->_class->invokeDefault) 127 retval = npObject->_class->invokeDefault(npObject, npArgs.get(), numArgs, &result); 128 break; 129 default: 130 break; 131 } 132 133 if (!retval) 134 throwError("Error calling method on NPObject.", V8Proxy::GeneralError); 135 136 for (int i = 0; i < numArgs; i++) 137 _NPN_ReleaseVariantValue(&npArgs[i]); 138 139 // Unwrap return values. 140 v8::Handle<v8::Value> returnValue = convertNPVariantToV8Object(&result, npObject); 141 _NPN_ReleaseVariantValue(&result); 142 143 return returnValue; 144 }
这个函数用于将JS中获取的JAVA对象和JS格式的参数(JAVA反射机制将使用这个对象和参数)转换为NPAPI中通用的结构体,并调用对应的Invoke函数,在Invoke函数中进一步处理后交由JAVA环境处理,也就是在JS和NPAPI中起到了一个桥梁作用。
Line106~107为参数打包,Line113~131为根据标识码调用对应的Invoke函数。
参数打包使用的函数在 /external/webkit/Source/WebCore/bindings/v8/V8NPUtils.cpp 中
Invoke函数在 /external/webkit/Source/WebCore/bridge/jni/v8/JavaNPObjectV8.cpp 中,也就是上面提到的bool JavaNPObjectInvoke(NPObject* obj, NPIdentifier identifier, const NPVariant* args, uint32_t argCount, NPVariant* result)
未完待续…
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题