您的位置:首页 > 移动开发 > Android开发

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进行交互。

// /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)

未完待续…
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android webview jni npapi js