Android浏览器插件开发

本人总结的类图:


最近在开发一个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的全局变量:

  1. ANPAudioTrackInterfaceV0    gSoundI;  
  2. ANPBitmapInterfaceV0        gBitmapI;  
  3. ANPCanvasInterfaceV0        gCanvasI;  
  4. ANPEventInterfaceV0         gEventI;  
  5. ANPLogInterfaceV0           gLogI;  
  6. ANPPaintInterfaceV0         gPaintI;  
  7. ANPPathInterfaceV0          gPathI;  
  8. ANPSurfaceInterfaceV0       gSurfaceI;  
  9. ANPSystemInterfaceV0        gSystemI;  
  10. ANPTypefaceInterfaceV0      gTypefaceI;  
  11. ANPWindowInterfaceV0        gWindowI;  

[cpp]  view plain copy
  1. ANPAudioTrackInterfaceV0    gSoundI;  
  2. ANPBitmapInterfaceV0        gBitmapI;  
  3. ANPCanvasInterfaceV0        gCanvasI;  
  4. ANPEventInterfaceV0         gEventI;  
  5. ANPLogInterfaceV0           gLogI;  
  6. ANPPaintInterfaceV0         gPaintI;  
  7. ANPPathInterfaceV0          gPathI;  
  8. ANPSurfaceInterfaceV0       gSurfaceI;  
  9. ANPSystemInterfaceV0        gSystemI;  
  10. ANPTypefaceInterfaceV0      gTypefaceI;  
  11. ANPWindowInterfaceV0        gWindowI;  

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

  1. static   const   struct  {  
  2.        NPNVariable     v;  
  3.        uint32_t        size;  
  4.        ANPInterface*   i;  
  5.    } gPairs[] = {  
  6.        { kAudioTrackInterfaceV0_ANPGetValue,   sizeof (gSoundI),    &gSoundI },  
  7.        { kBitmapInterfaceV0_ANPGetValue,       sizeof (gBitmapI),   &gBitmapI },  
  8.        { kCanvasInterfaceV0_ANPGetValue,       sizeof (gCanvasI),   &gCanvasI },  
  9.        { kEventInterfaceV0_ANPGetValue,        sizeof (gEventI),    &gEventI },  
  10.        { kLogInterfaceV0_ANPGetValue,          sizeof (gLogI),      &gLogI },  
  11.        { kPaintInterfaceV0_ANPGetValue,        sizeof (gPaintI),    &gPaintI },  
  12.        { kPathInterfaceV0_ANPGetValue,         sizeof (gPathI),     &gPathI },  
  13.        { kSurfaceInterfaceV0_ANPGetValue,      sizeof (gSurfaceI),  &gSurfaceI },  
  14.        { kSystemInterfaceV0_ANPGetValue,       sizeof (gSystemI),   &gSystemI },  
  15.        { kTypefaceInterfaceV0_ANPGetValue,     sizeof (gTypefaceI), &gTypefaceI },  
  16.        { kWindowInterfaceV0_ANPGetValue,       sizeof (gWindowI),   &gWindowI },  
  17.    };  
  18.    for  ( size_t  i = 0; i < ARRAY_COUNT(gPairs); i++) {  
  19.        gPairs[i].i->inSize = gPairs[i].size;  
  20.        NPError err = browser->getvalue(NULL, gPairs[i].v, gPairs[i].i);  
  21.        if  (err) {  
  22.            return  err;  
  23.        }  
  24.    }  

[cpp]  view plain copy
  1. static const struct {  
  2.        NPNVariable     v;  
  3.        uint32_t        size;  
  4.        ANPInterface*   i;  
  5.    } gPairs[] = {  
  6.        { kAudioTrackInterfaceV0_ANPGetValue,   sizeof(gSoundI),    &gSoundI },  
  7.        { kBitmapInterfaceV0_ANPGetValue,       sizeof(gBitmapI),   &gBitmapI },  
  8.        { kCanvasInterfaceV0_ANPGetValue,       sizeof(gCanvasI),   &gCanvasI },  
  9.        { kEventInterfaceV0_ANPGetValue,        sizeof(gEventI),    &gEventI },  
  10.        { kLogInterfaceV0_ANPGetValue,          sizeof(gLogI),      &gLogI },  
  11.        { kPaintInterfaceV0_ANPGetValue,        sizeof(gPaintI),    &gPaintI },  
  12.        { kPathInterfaceV0_ANPGetValue,         sizeof(gPathI),     &gPathI },  
  13.        { kSurfaceInterfaceV0_ANPGetValue,      sizeof(gSurfaceI),  &gSurfaceI },  
  14.        { kSystemInterfaceV0_ANPGetValue,       sizeof(gSystemI),   &gSystemI },  
  15.        { kTypefaceInterfaceV0_ANPGetValue,     sizeof(gTypefaceI), &gTypefaceI },  
  16.        { kWindowInterfaceV0_ANPGetValue,       sizeof(gWindowI),   &gWindowI },  
  17.    };  
  18.    for (size_t i = 0; i < ARRAY_COUNT(gPairs); i++) {  
  19.        gPairs[i].i->inSize = gPairs[i].size;  
  20.        NPError err = browser->getvalue(NULL, gPairs[i].v, gPairs[i].i);  
  21.        if (err) {  
  22.            return err;  
  23.        }  
  24.    }  

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

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

[cpp]  view plain copy
  1. 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值