上一节简单介绍了WebKit加载插件的过程,这一节开始介绍插件的具体开发方法
在Android平台开发插件,下面这4个函数是必须实现的
NPError NP_Initialize(NPNetscapeFuncs* browserFuncs, NPPluginFuncs* pluginFuncs, void *java_env);
NPError NP_GetValue(NPP instance, NPPVariable variable, void *value);
const char* NP_GetMIMEDescription(void);
void NP_Shutdown(void);
注:这个在不同的平台下面有区别,我发现在mac os上面需要实现
NPError NP_Initialize(NPNetscapeFuncs *browserFuncs);
NPError NP_GetEntryPoints(NPPluginFuncs *pluginFuncs);
void NP_Shutdown(void);
这3个函数,比较一下参数就知道,Android下面的
NP_Initialize
包括了mac os中的
NP_Initialize
和
NP_GetEntryPoints
的功能,额外的java_env是将java虚拟机环境传下来,作为jni层的context,这个是Android必须要的,你懂的
但mac os为啥不需要实现
NP_GetMIMEDescription
呢?嗯,这个俺没去深究,有谁有兴趣的可以去研究一下
下面看看
const char* NP_GetMIMEDescription(void);
比如返回:"application/x-yourname:tst:MIME type is application/x-yourname"
可以参考
NP_Initialize
比较重要,通过browserFuncs将浏览器的接口传给插件,通过pluginFuncs得到插件的接口,java_env上面提过了
问题:为啥用这种方式将接口传给插件呢?
一般来说,公布模块的接口,我们会直接声明函数的,要使用它,包含头文件,库文件就可以直接调用了,但这里有个问题,我们开发的是插件,是给浏览器用的,而不是直接调用浏览器。从语法上说明可能更清楚一些,在Android上面这些插件都是app,并包含动态库.so文件,按照一般的调用方式,将提示找不到这些接口的实现,是的,用函数指针将解决这个问题,在插件中访问浏览器的接口都是通过函数指针实现的,感觉有点类似C++中的虚函数,万能的指针,C的灵魂啊
问题:那为啥插件的接口传给浏览器也要用这种方式呢?
对啊,既然调用是从浏览器到插件的,那插件直接提供头文件,浏览器直接调用不就好了,为啥又要用函数指针?这个我也没整明白,貌似用直接调用的方式也是可行的,比如我们在讨论的这4个函数就是从浏览器直接调用插件的,不知道大家有没有别的答案
下面分析一下参数browserFuncs,在npfunctions.h中定义的:
typedef struct _NPNetscapeFuncs {
uint16 size;
uint16 version;
NPN_GetURLProcPtr geturl;
NPN_PostURLProcPtr posturl;
NPN_RequestReadProcPtr requestread;
NPN_NewStreamProcPtr newstream;
NPN_WriteProcPtr write;
NPN_DestroyStreamProcPtr destroystream;
NPN_StatusProcPtr status;
NPN_UserAgentProcPtr uagent;
NPN_MemAllocProcPtr memalloc;
NPN_MemFreeProcPtr memfree;
NPN_MemFlushProcPtr memflush;
NPN_ReloadPluginsProcPtr reloadplugins;
NPN_GetJavaEnvProcPtr getJavaEnv;
NPN_GetJavaPeerProcPtr getJavaPeer;
NPN_GetURLNotifyProcPtr geturlnotify;
NPN_PostURLNotifyProcPtr posturlnotify;
NPN_GetValueProcPtr getvalue;
NPN_SetValueProcPtr setvalue;
NPN_InvalidateRectProcPtr invalidaterect;
NPN_InvalidateRegionProcPtr invalidateregion;
NPN_ForceRedrawProcPtr forceredraw;
NPN_GetStringIdentifierProcPtr getstringidentifier;
NPN_GetStringIdentifiersProcPtr getstringidentifiers;
NPN_GetIntIdentifierProcPtr getintidentifier;
NPN_IdentifierIsStringProcPtr identifierisstring;
NPN_UTF8FromIdentifierProcPtr utf8fromidentifier;
NPN_IntFromIdentifierProcPtr intfromidentifier;
NPN_CreateObjectProcPtr createobject;
NPN_RetainObjectProcPtr retainobject;
NPN_ReleaseObjectProcPtr releaseobject;
NPN_InvokeProcPtr invoke;
NPN_InvokeDefaultProcPtr invokeDefault;
NPN_EvaluateProcPtr evaluate;
NPN_GetPropertyProcPtr getproperty;
NPN_SetPropertyProcPtr setproperty;
NPN_RemovePropertyProcPtr removeproperty;
NPN_HasPropertyProcPtr hasproperty;
NPN_HasMethodProcPtr hasmethod;
NPN_ReleaseVariantValueProcPtr releasevariantvalue;
NPN_SetExceptionProcPtr setexception;
NPN_PushPopupsEnabledStateProcPtr pushpopupsenabledstate;
NPN_PopPopupsEnabledStateProcPtr poppopupsenabledstate;
NPN_EnumerateProcPtr enumerate;
NPN_PluginThreadAsyncCallProcPtr pluginthreadasynccall;
NPN_ConstructProcPtr construct;
NPN_GetValueForURLProcPtr getvalueforurl;
NPN_SetValueForURLProcPtr setvalueforurl;
NPN_GetAuthenticationInfoProcPtr getauthenticationinfo;
NPN_ScheduleTimerProcPtr scheduletimer;
NPN_UnscheduleTimerProcPtr unscheduletimer;
NPN_PopUpContextMenuProcPtr popupcontextmenu;
NPN_ConvertPointProcPtr convertpoint;
} NPNetscapeFuncs;
有超过50个函数。。。。。我没找到详细的说明文档,那就以后碰到了再研究吧,有个概念,里面提供了浏览器端的各种接口,提供给插件使用
下面是pluginFuncs的:
typedef struct _NPPluginFuncs {
uint16 size;
uint16 version;
NPP_NewProcPtr newp;
NPP_DestroyProcPtr destroy;
NPP_SetWindowProcPtr setwindow;
NPP_NewStreamProcPtr newstream;
NPP_DestroyStreamProcPtr destroystream;
NPP_StreamAsFileProcPtr asfile;
NPP_WriteReadyProcPtr writeready;
NPP_WriteProcPtr write;
NPP_PrintProcPtr print;
NPP_HandleEventProcPtr event;
NPP_URLNotifyProcPtr urlnotify;
JRIGlobalRef javaClass;
NPP_GetValueProcPtr getvalue;
NPP_SetValueProcPtr setvalue;
} NPPluginFuncs;
这个只有13个函数,比较好看哈,我也没找到详细的说明文档。。。。但看名字大概可以猜出作用,这些都是插件提供给浏览器的回调,浏览器会在需要的时候调用它们,比如创建插件的时候调用NPP_NewProcPtr,销毁的时候调用NPP_DestroyProcPtr等
在这里,Android为我们提供了一套接口,扩展了浏览器端的接口,定义在Android_npapi.h中:
#define kLogInterfaceV0_ANPGetValue ((NPNVariable)1000)
#define kAudioTrackInterfaceV0_ANPGetValue ((NPNVariable)1001)
#define kCanvasInterfaceV0_ANPGetValue ((NPNVariable)1002)
#define kMatrixInterfaceV0_ANPGetValue ((NPNVariable)1003)
#define kPaintInterfaceV0_ANPGetValue ((NPNVariable)1004)
#define kPathInterfaceV0_ANPGetValue ((NPNVariable)1005)
#define kTypefaceInterfaceV0_ANPGetValue ((NPNVariable)1006)
#define kWindowInterfaceV0_ANPGetValue ((NPNVariable)1007)
#define kBitmapInterfaceV0_ANPGetValue ((NPNVariable)1008)
#define kSurfaceInterfaceV0_ANPGetValue ((NPNVariable)1009)
#define kSystemInterfaceV0_ANPGetValue ((NPNVariable)1010)
#define kEventInterfaceV0_ANPGetValue ((NPNVariable)1011)
#define kSupportedDrawingModel_ANPGetValue ((NPNVariable)2000)
#define kJavaContext_ANPGetValue ((NPNVariable)2001)
上面列出的是ANPGetValue使用到的类型参数,分别可以得到相关的接口,比如Log,播放音频,绘图,控制视图,得到当前Context等等,这些接口的实现都是由WebKit引擎做的,其中有个ANPEventInterfaceV0,post事件会在NPP_HandleEventProcPtr中得到,相当于引擎提供的一个消息分发中心,这里负责分发各种系统消息,包括鼠标,键盘,触摸,绘制等,用户也可以自定义消息,具体请参考android_npapi.h,这里的消息将发送给具体的插件,后面将介绍具体的插件是怎么生成的
注意一下上面接口的NPX前缀,NPN表示浏览器端的接口(Netscape-Plugin-Netscape?),而NPP表示插件端的接口(Netscape-Plugin-Plugin?),那最开始的NP接口呢?嗯,不清楚,可能只是为了和这两种区别开来
OK,到了这里,浏览器通过MIME找到了插件,通过函数指针,告诉了插件,我浏览器提供的接口,也得到了插件提供的接口,顺便也把java环境Context传给了插件