最近在开发一个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 ?
  1. ANPAudioTrackInterfaceV0gSoundI;
  2. ANPBitmapInterfaceV0gBitmapI;
  3. ANPCanvasInterfaceV0gCanvasI;
  4. ANPEventInterfaceV0gEventI;
  5. ANPLogInterfaceV0gLogI;
  6. ANPPaintInterfaceV0gPaintI;
  7. ANPPathInterfaceV0gPathI;
  8. ANPSurfaceInterfaceV0gSurfaceI;
  9. ANPSystemInterfaceV0gSystemI;
  10. ANPTypefaceInterfaceV0gTypefaceI;
  11. ANPWindowInterfaceV0gWindowI;

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 ?
  1. static const struct {
  2. NPNVariablev;
  3. uint32_tsize;
  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. NPErrorerr=browser->getvalue(NULL,gPairs[i].v,gPairs[i].i);
  21. if (err){
  22. return err;
  23. }
  24. }

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 ?
  1. gLogI.log(kDebug_ANPLogType, "------%pDrawingModelis%d" ,instance,model);

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

更多相关文章

  1. 箭头函数的基础使用
  2. Python技巧匿名函数、回调函数和高阶函数
  3. android之Binder机制解析
  4. [Android] AIDL的使用情况和实例介绍
  5. Android——自定义View类(一 )
  6. Android与JNI(一)
  7. 初试Android高性能编程OpenCL
  8. Android输入子系统之InputReader读取键盘消息过程分析
  9. Android应用程序与SurfaceFlinger服务之间的共享UI元数据(SharedC

随机推荐

  1. Android下如何杀死后台进程
  2. 分析(Android下的任务和Activity栈)
  3. Android新版NDK环境配置(免Cygwin)
  4. Android中设计模式无处不在之单例模式
  5. android之xml数据解析(DOM)
  6. Android(安卓)上实现非root的 Traceroute
  7. Emulator(2)Choose and Configure AVD an
  8. mldn android
  9. Android(安卓)Animations 3D flip
  10. Android(安卓)博文积累