最近在开发一个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. 使用浏览器查看Android SQLite数据库-Android Debug Database用
  2. Android sql数据库的Android包里面的函数介绍
  3. Android : RadioBotton—— 图片浏览器
  4. Android:Serializable接口和Parcelable接口
  5. 【Android开发】Android Studio中进行简单的WebView构建浏览器开
  6. Android中调用C++函数的一个简单Demo
  7. 使用c#开发的第一款APP的Android浏览器(创世纪篇)
  8. 使用android中drawline函数无法绘制水平线的解决办法
  9. 第三部分:Android 应用程序接口指南---第一节:应用程序组件---第二

随机推荐

  1. Android App性能信息获取方法
  2. 物流货运移动APP解决方案
  3. Android开发重要参考资料
  4. Android调用百度地图Web端接口,实现百度定
  5. 解决Android Studio运行编译时间久的最有
  6. 直接拿来用!十大Material Design开源项目
  7. Android Studio获取数字签名(SHA1)的方法
  8. Android Volley:使用方法总结及实例解析
  9. Android之用Handler实现主线程和子线程互
  10. Android 歌词Lrc显示 自定义View