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

Android浏览器插件开发

2013-10-19 15:54 381 查看
本人总结的类图:



最近在开发一个Android上的浏览器插件,因此总结了一些经验。

首先,我们应该对Netscape Plugin API有一定的了解,相关的资料可以查阅这个网页:https://developer.mozilla.org/en/Gecko_Plugin_API_Reference

值得庆幸的是,Android的源码目录下提供了Plugin的范例,早期版本的源码包中,这个例子在external/webkit /WebKit/android/plugins/sample,但这个例子应该说不是太完整的,只包含了.so的生成,后来的源代码包中这个 sample目录被删除了,取而代之的是development/samples/BrowserPlugin,通过这个版本的例子编译生成的是完整的 apk安装包,可以在模拟器或者真机上安装测试。

我们可以看一看BrowserPlugin的目录:

jni目录,这个目录是插件的主体,Native C/C++写的Shared Library,负责NPAPI中NPP侧的实现。新版的plugin例子采用了子插件的结构,提供了五种类型的子插件,我们可以根据自己的需要进行参考。

res目录,和一般的android工程一样,存放资源的目录。

src目录,java代码的目录,实现了一个service类,并对surface绘制方式的plugin提供java的接口。

AndroidManifest.xml,同样是每个android的工程都会有文件,包含了apk的注册信息,就在这里实现pluging的注册。

Android.mk,编译配置文件。

通过这个例子,我们可以看到一个基本的plugin是如何实现的。对于plugin的更详细的说明将在以后阐述。

 

 

在写Android的browser plugin的时候,需要实现一系列的NPP函数,关于函数的具体说明还是推荐看一看这个网页:https://developer.mozilla.org/en/Gecko_Plugin_API_Reference

下面说一说在这些函数中需要完成的任务

//===================================
NPError NP_Initialize(NPNetscapeFuncs*,

                                     NPPluginFuncs*,

                                     JNIEnv *java_environment,

                                     jobject application_context);
        Plugin初始化函数,浏览器会通过参数传进一个浏览器侧的NPAPI函数列表(NPN函数列表),plugin需要在这里实现全局参数的初始化,并返 回plugin侧的NPAPI函数列表(NPP函数列表)。Android的Plugin可以通过NPN_GetValue获取浏览器参数以及 Android提供的各种操作接口(ANP Inerface),Android提供的操作接口可以查看源代码的这一部分:external/webkit/WebKit/android /plugins。Android的NP_Initialize还提供了上层的java运行环境,可用于实现与java侧的交互。

 //===================================

void NP_Shutdown(); 

        关闭Plugin,浏览器在销毁了所有plugin实例以后就会调用这个函数,可以在这里释放一些全局的资源。

 //===================================

NPError NPP_New(NPMIMEType pluginType,

                                 NPP instance, uint16 mode,

                                 int16 argc, char *argn[],

                                 char *argv[], NPSavedData *saved); 

        新建一个实例,浏览器每创建一个plugin的实例就会调用一次这个函数。在这里主要就是根据传进的参数列表进行实例的初始化,建立新的Plugin对 象,并通过NPN_SetValue告知浏览器plugin对象的一些特性,其中包括了plugin对象能处理的事件(触控事件和按键事件),以及 plugin的渲染模式(bitmap模式或surface模式)。

 //===================================

NPError NPP_Destroy(NPP instance,

                                      NPSavedData** save); 

        当浏览器需要销毁一个plugin实例的时候调用,要在这里完成这对应实例的资源释放。

 //===================================

NPError NP_GetValue(NPP instance,

                                      NPPVariable variable,

                                      void *ret_value); 

        浏览器通过此函数获取plugin的一些全局参数,主要是plugin的名称和描述。

 //===================================

NPError NPP_GetValue(NPP instance,

                                        NPPVariable variable,

                                        void *ret_value); 

        浏览器通过此函数获取plugin对象的一些参数,需要根据NPPVariable variable进行不同的处理,NPPVariable的定义可以参照external/webkit/Webcore/bridge/npapi.h 和external/webkit/WebKit/android/plugins/android_npapi.h。

 //===================================

NPError NPP_SetValue(NPP instance,

                                        NPNVariable variable,

                                        void *value); 

        浏览器通过此函数设置plugin对象的一些参数,和NPP_GetValue一样,需要根据NPPVariable variable进行不同的处理,NPPVariable的定义可以参照external/webkit/Webcore/bridge/npapi.h 和external/webkit/WebKit/android/plugins/android_npapi.h。

 //===================================

NPError NPP_SetWindow(NPP instance,

                                            NPWindow* window); 

        浏览器通过该函数告知plugin对象其窗口参数,主要就是的plugin对象所占画面的大小。

 //===================================

NPError NPP_NewStream(NPP instance,

                                             NPMIMEType type,

                                             NPStream* stream,

                                             NPBool seekable,

                                             uint16* stype); 

        如果需要向plugin传输一些流数据,浏览器会通过此函数告知plugin即将要传输的流,在参数NPStream* stream中包含了流的url,以后需要对根据此url对NPP_Write传入的数据进行区分。

 //===================================

void NPP_StreamAsFile(NPP instance,

                                         NPStream* stream,

                                         const char* fname);  

        如果浏览器要传输的是本地文件流,则会选择调用这个参数通知plugin流的信息。

 //===================================

NPError NPP_DestroyStream(NPP instance,

                                                  NPStream* stream,

                                                  NPReason reason); 

        如果数据流传输结束或意外终止了,浏览器会调用此函数告知plugin注销这一数据流,可以通过NPReason reason判断数据流是否为正常结束。

 //===================================

int32 NPP_WriteReady(NPP instance,

                                        NPStream* stream); 

        浏览器在给plugin对象传输流数据前,会先调用这一函数询问plugin能接收的数据长度。

 //===================================

int32 NPP_Write(NPP instance,

                              NPStream* stream,

                              int32_t offset, int32_t len,

                              void* buffer); 

        流数据的传输,根据 NPStream* stream里的url可以判断是哪个数据流,int32_t offset为void* buffer这段数据在数据流中的偏移量,int32_t len为void* buffer的长度,返回值是plugin对象实际接收的数据大小。

 //===================================

int16 NPP_HandleEvent(NPP instance,

                                          void* event); 

        事件处理函数,在这里plugin要完成各种事件的处理,包括绘制、按键、鼠标、触控等等,事件的参数都包装在void* event里,可以参照external/webkit/WebKit/android/plugins/android_npapi.h中 ANPEvent结构体的定义。

 //===================================

void NPP_Print(NPP instance,

                           NPPrint* platformPrint) 

        根据NPAPI的定义,浏览器会通过这个函数通知plugin进行输出操作。

 //===================================

void NPP_URLNotify(NPP instance,

                                    const char* URL,

                                    NPReason reason,

                                    void* notifyData);  

        如果plugin调用了NPN_GetURLNotify或者NPN_PostURLNotify,在浏览器侧的操作完成了以后,就会调用这个函数返回一些信息。

 //===================================

 

今天讲一下ANPInterface。

大概是为了弥补NPAPI在Android上的不足,Google在Android的浏览器上实现了ANPInterface这么一个东 西。说白了这玩意就是一系列的操作接口(函数),提供了一些NPAPI没有的东西。插件可以在初始化的时候获取这些ANPXXXInterface,并在 运行过程中使用。

关于这些接口的使用,大家可以看一看作为例子的BrowserPlugin是怎么做的,

main.cpp中,声明了一些ANPInterface的全局变量:

view
plain copy
to clipboard print ?

ANPAudioTrackInterfaceV0    gSoundI;  

ANPBitmapInterfaceV0        gBitmapI;  

ANPCanvasInterfaceV0        gCanvasI;  

ANPEventInterfaceV0         gEventI;  

ANPLogInterfaceV0           gLogI;  

ANPPaintInterfaceV0         gPaintI;  

ANPPathInterfaceV0          gPathI;  

ANPSurfaceInterfaceV0       gSurfaceI;  

ANPSystemInterfaceV0        gSystemI;  

ANPTypefaceInterfaceV0      gTypefaceI;  

ANPWindowInterfaceV0        gWindowI;  

[cpp] view
plaincopy

ANPAudioTrackInterfaceV0    gSoundI;  

ANPBitmapInterfaceV0        gBitmapI;  

ANPCanvasInterfaceV0        gCanvasI;  

ANPEventInterfaceV0         gEventI;  

ANPLogInterfaceV0           gLogI;  

ANPPaintInterfaceV0         gPaintI;  

ANPPathInterfaceV0          gPathI;  

ANPSurfaceInterfaceV0       gSurfaceI;  

ANPSystemInterfaceV0        gSystemI;  

ANPTypefaceInterfaceV0      gTypefaceI;  

ANPWindowInterfaceV0        gWindowI;  

下面则是中NP_Initialize里面的一段:

view
plain copy
to clipboard print ?

static   const   struct  {  

       NPNVariable     v;  

       uint32_t        size;  

       ANPInterface*   i;  

   } gPairs[] = {  

       { kAudioTrackInterfaceV0_ANPGetValue,   sizeof (gSoundI),    &gSoundI },  

       { kBitmapInterfaceV0_ANPGetValue,       sizeof (gBitmapI),   &gBitmapI },  

       { kCanvasInterfaceV0_ANPGetValue,       sizeof (gCanvasI),   &gCanvasI },  

       { kEventInterfaceV0_ANPGetValue,        sizeof (gEventI),    &gEventI },  

       { kLogInterfaceV0_ANPGetValue,          sizeof (gLogI),      &gLogI },  

       { kPaintInterfaceV0_ANPGetValue,        sizeof (gPaintI),    &gPaintI },  

       { kPathInterfaceV0_ANPGetValue,         sizeof (gPathI),     &gPathI },  

       { kSurfaceInterfaceV0_ANPGetValue,      sizeof (gSurfaceI),  &gSurfaceI },  

       { kSystemInterfaceV0_ANPGetValue,       sizeof (gSystemI),   &gSystemI },  

       { kTypefaceInterfaceV0_ANPGetValue,     sizeof (gTypefaceI), &gTypefaceI },  

       { kWindowInterfaceV0_ANPGetValue,       sizeof (gWindowI),   &gWindowI },  

   };  

   for  ( size_t  i = 0; i < ARRAY_COUNT(gPairs); i++) {  

       gPairs[i].i->inSize = gPairs[i].size;  

       NPError err = browser->getvalue(NULL, gPairs[i].v, gPairs[i].i);  

       if  (err) {  

           return  err;  

       }  

   }  

[cpp] view
plaincopy

static const struct {  

       NPNVariable     v;  

       uint32_t        size;  

       ANPInterface*   i;  

   } gPairs[] = {  

       { kAudioTrackInterfaceV0_ANPGetValue,   sizeof(gSoundI),    &gSoundI },  

       { kBitmapInterfaceV0_ANPGetValue,       sizeof(gBitmapI),   &gBitmapI },  

       { kCanvasInterfaceV0_ANPGetValue,       sizeof(gCanvasI),   &gCanvasI },  

       { kEventInterfaceV0_ANPGetValue,        sizeof(gEventI),    &gEventI },  

       { kLogInterfaceV0_ANPGetValue,          sizeof(gLogI),      &gLogI },  

       { kPaintInterfaceV0_ANPGetValue,        sizeof(gPaintI),    &gPaintI },  

       { kPathInterfaceV0_ANPGetValue,         sizeof(gPathI),     &gPathI },  

       { kSurfaceInterfaceV0_ANPGetValue,      sizeof(gSurfaceI),  &gSurfaceI },  

       { kSystemInterfaceV0_ANPGetValue,       sizeof(gSystemI),   &gSystemI },  

       { kTypefaceInterfaceV0_ANPGetValue,     sizeof(gTypefaceI), &gTypefaceI },  

       { kWindowInterfaceV0_ANPGetValue,       sizeof(gWindowI),   &gWindowI },  

   };  

   for (size_t i = 0; i < ARRAY_COUNT(gPairs); i++) {  

       gPairs[i].i->inSize = gPairs[i].size;  

       NPError err = browser->getvalue(NULL, gPairs[i].v, gPairs[i].i);  

       if (err) {  

           return err;  

       }  

   }  

该段的目的就是通过浏览器NPN接口里面的getvalue获取一系列ANPInterface。由于定义为了全局变量,所以这些接口可以在随时随地使用,就像这样:

view
plain copy
to clipboard print ?

gLogI.log(kDebug_ANPLogType,  "------ %p DrawingModel is %d" , instance, model);  

[cpp] view
plaincopy

gLogI.log(kDebug_ANPLogType, "------ %p DrawingModel is %d", instance, model);  

下面就我所知道的情况说说这些接口都提供了哪些操作。

ANPAudioTrackInterface      这是一套音频接口,提供了播放音轨的功能

ANPBitmapInterface          只有一个函数getPixelPacking,依照给定的格式设置PixelPacking的参数值

ANPCanvasInterface          提供了一系列ANPCanvas绘图操作,其实就是把skia的Skcanvas相关接口作了一个包装

ANPEventInterface           提供了一个postEvent函数,插件可以用来向自己发送自定义的消息(ANPEvent)。

ANPLogInterface             提供了logcat的输出

ANPPaintInterface           包装了skia的SkPaint的相关接口,如果要用ANPCanvas画图,那就可能需要用这些接口来设置ANPPaint参数

ANPMatrixInterface          提供了ANPMatrix(SkMatrix)的一些操作接口,用ANPCanvas画图时可能会用到

ANPPathInterface            提供了ANPPath(SkPath)的一些操作接口,用ANPCanvas画图时可能会用到

ANPSurfaceInterface         提供了从SurfaceView中获取画布ANPBitmap的接口lock,以及提交绘图结果的接口unlock

ANPSystemInterface          getApplicationDataDirectory可以获取一个叫做PluginSharedDataDirectory的地址,具体我也没试 过;2.2中新增了一个接口loadJavaClass,用于获取Java Class的实例,这个主要是用在加载过程中View的实例化。

ANPTypefaceInterface        这个我不是太清楚,似乎又是skia中一些功能的封装,大概和字体有关

ANPWindowInterface          一些窗口操作的接口,包括显示软键盘、全屏控制等等

具体定义可以看external/webkit/WebKit/android/plugins下的相关文件

使用方法可以参考development/samples/BrowserPlugin这个例子。

可以看出,其实ANPInterface提供的接口,其实现大多来自webkit以外的一些底层库。或许有人会问,这和直接连接这些库有什 么区别?其实从结果上看,这两种方法都是殊途同归的,不过ANPInterface更多地体现为一种包装,就算底层库有了变动,只要 ANPInterface不变,插件的代码就不需要修改。例如从Android 2.1到2.2,surfaceflinger有了较大的变化,但是ANPSurfaceInterface没有改变,因此插件也就不需要对这部分作什么 修改。不过实际使用过程中,还是根据自己的需要去选择用ANPInterface还是直接连接外部库吧,毕竟ANPInterface提供的接口还是很有
限的。

 

转自http://blog.csdn.net/qyqzj/archive/2010/05/23/5617220.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: