320 likes | 457 Views
第十一章 Android 浏览器扩展. 本章主要内容. NPAPI 简介. Netscape Plugin Application Programming Interface 是一个被许多浏览器遵循和采用的跨平台的插件框架。 NPAPI 的接口分为两组:浏览器侧的 NPN 接口和插件侧的 NPP 接口。 浏览器插件的核心,就是一个实现了 NPP 接口,并使用浏览器提供的 NPN APIs 进行对外操作的动态库。. NPAPI 简介.
E N D
NPAPI简介 • Netscape Plugin Application Programming Interface是一个被许多浏览器遵循和采用的跨平台的插件框架。 • NPAPI的接口分为两组:浏览器侧的 NPN 接口和插件侧的 NPP 接口。 • 浏览器插件的核心,就是一个实现了 NPP 接口,并使用浏览器提供的 NPN APIs 进行对外操作的动态库。
NPAPI简介 • Android 浏览器插件特有的结构:插件的 Java 层。这样,浏览器插件就可以作为一个Android应用,通过常规途径安装到Android设备中。(Android的所有应用都必须通过Java 部分实现安装)
Android中的浏览器插件开发分析 BrowserPlugin • Android的源码目录下提供了Plugin的范例: development/samples/BrowserPlugin; • 通过这个版本的例子编译生成的是完整的apk安装包,可以在模拟器或者真机上安装测试。
Android中的浏览器插件开发分析 BrowserPlugin结构 • jni目录是插件的主体,Native C/C++写的Shared Library,负责NPAPI中NPP侧的实现。下有5种子插件目录(animation,audio,background,form,paint), Android.mk文件(Make文件) ,hello-jni.cpp文件 (注册java本地接口,hello-world函数,测试用),jni-bridge.cpp文件(注册java本地接口,注册的函数在SamplePluginStub.java中调用) ,main.cpp文件(实现NPP接口) ,main.h文件(定义 NPP接口变量) ,PluginObject.cpp文件(插件的基类),PluginObject.h文件 。
Android中的浏览器插件开发分析 jni目录静态结构
Android中的浏览器插件开发分析 jni目录静态结构
Android中的浏览器插件开发分析 BrowserPlugin结构 • res目录,和一般的android工程一样,存放资源的目录。 • src目录下有实现了一个android的service类(其他大部分插件是实现activity类),并为插件提供绘制接口;有SamplePluginStub.java、SamplePlugin.java: 实现服务接口、Cube.java、CubeRenderer.java文件。 • AndroidManifest.xml,同样是每个android的工程都会有文件,包含了apk的注册信息,就在这里实现plugin的注册。 • Android.mk,编译配置文件。
Android中的浏览器插件开发分析 NPP APIs • NPErrorNPP_New(NPMIMETypepluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved); • 新建一个实例,浏览器每创建一个plugin的实例就会调用一次这个函数。通过NPN_SetValue告知浏览器plugin对象的一些特性,其中包括了plugin对象能处理的事件(触控事件和按键事件),以及plugin的渲染模式(bitmap模式或surface模式)。
Android中的浏览器插件开发分析 NPP APIs • NPErrorNPP_Destroy(NPP instance, NPSavedData** save); • 当浏览器需要销毁一个plugin实例的时候调用,要在这里完成对应实例的资源释放。 • NPErrorNPP_SetWindow(NPP instance, NPWindow* window); • 浏览器通过该函数告知plugin对象其窗口参数,主要就是的plugin对象所占画面的大小。
Android中的浏览器插件开发分析 NPP APIs • NPErrorNPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBoolseekable, uint16* stype); • 如果需要向plugin传输一些流数据,浏览器会通过此函数告知plugin即将要传输的流,在参数NPStream* stream中包含了流的url,以后需要对根据此url对NPP_Write传入的数据进行区分。
Android中的浏览器插件开发分析 NPP APIs • NPErrorNPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason); • 如果数据流传输结束或意外终止了,浏览器会调用此函数告知plugin注销这一数据流,可以通过NPReason reason判断数据流是否为正常结束。 • int32 NPP_WriteReady(NPP instance, NPStream* stream); • 浏览器在给plugin对象传输流数据前,会先调用这一函数询问plugin能接收的数据长度。
Android中的浏览器插件开发分析 NPP APIs • int32 NPP_Write(NPP instance, NPStream* stream, int32 offset, int32 len,void* buffer); • 流数据的传输,根据 NPStream* stream里的url可以判断是哪个数据流,int32_t offset为void* buffer这段数据在数据流中的偏移量,int32_t len为void* buffer的长度,返回值是plugin对象实际接收的数据大小。 • void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname); • 如果浏览器要传输的是本地文件流,则会选择调用这个参数通知plugin流的信息。
Android中的浏览器插件开发分析 NPP APIs • void NPP_Print(NPP instance, NPPrint* platformPrint); • 根据NPAPI的定义,浏览器会通过这个函数通知plugin进行输出操作。 • int16 NPP_HandleEvent(NPP instance, void* event); • 事件处理函数,在这里plugin要完成各种事件的处理,包括绘制、按键、鼠标、触控等等,事件的参数都包装在void* event里,可以参照external/webkit/WebKit/android/plugins/android_npapi.h中ANPEvent结构体的定义。
Android中的浏览器插件开发分析 NPP APIs • void NPP_URLNotify(NPP instance, const char* URL, NPReason reason, void* notifyData); • 如果plugin调用了NPN_GetURLNotify或者NPN_PostURLNotify,在浏览器侧的操作完成了以后,就会调用这个函数返回一些信息。
Android中的浏览器插件开发分析 NPP APIs • NPErrorNPP_GetValue(NPP instance, NPPVariable variable, void *value); • 浏览器通过此函数获取plugin对象的一些参数,需要根据NPPVariable variable进行不同的处理,NPPVariable的定义可以参照external/webkit/Webcore/bridge/npapi.h和external/webkit/WebKit/android/plugins/android_npapi.h。 • NPErrorNPP_SetValue(NPP instance, NPNVariable variable, void *value);
Android中的浏览器插件开发分析 NPP APIs • NPErrorNPP_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。
Android中的浏览器插件开发分析 • ANPInterface • 为了弥补NPAPI在Android上的不足,Google在Android的浏览器上实现了ANPInterface这么一个东西。就是一系列的操作接口(函数),提供了一些NPAPI没有的东西。插件可以在初始化的时候获取这些ANPXXXInterface,并在运行过程中使用。 • 其实ANPInterface提供的接口,大多来自webkit的一些底层库(external/webkit/WebKit/android/plugins) • BrowserPlugin中的ANPInterface列表如下:
Android中的浏览器插件开发分析 • ANPInterface • ANPAudioTrackInterfaceV0 gSoundI; • ANPBitmapInterfaceV0 gBitmapI; • ANPCanvasInterfaceV0 gCanvasI; • ANPLogInterfaceV0 gLogI; • ANPPaintInterfaceV0 gPaintI; • ANPPathInterfaceV0 gPathI; • ANPSurfaceInterfaceV0 gSurfaceI; • ANPSystemInterfaceV0 gSystemI; • ANPTypefaceInterfaceV0 gTypefaceI; • ANPWindowInterfaceV0 gWindowI;
Android中的浏览器插件开发分析 NP_Initialize • 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侧的交互。
Android中的浏览器插件开发分析 NP_Shutdown • 关闭Plugin,浏览器在销毁了所有plugin实例以后就会调用这个函数,可以在这里释放一些全局的资源。
Android中的浏览器插件开发分析 • 编译BrowserPlugin • 修改jni/main.cpp文件之后编译(主要是增加LOGCAT调试信息),以方便后面分析插件加载流程。 • 进入源码根目录下 • 运行make SampleBrowserPlugin
Android中的浏览器插件开发分析 • 安装BrowserPlugin • 运行 “adb install [apk_file]” ,把编译好的插件apk安装到设备或模拟器中 • 安装成功后,你可以通过“Settings -> Applications -> Manage applications”管理插件 • 用包含以下内容的HTML页面测试浏览器插件 <object type="application/x-testbrowserplugin" height=50 width=250> <param name="DrawingModel" value="Surface" /> <param name="PluginType" value="Background" /> </object>
Android中的浏览器插件开发分析 • 运行测试 • 用浏览器打开测试网页,将会打印类似以下log: • D/plugin ( 366): *** NP_Initialize *** • D/plugin ( 366): *** 0x420f18 START NPP_New *** • D/plugin ( 366): ------ 0x420f18 DrawingModel is 1 • D/plugin ( 366): Application data dir is /data/data/com.android.browser/app_plugins • E/plugin ( 366): ------ 0x420f18 Testing Log Error • W/plugin ( 366): ------ 0x420f18 Testing Log Warning • D/plugin ( 366): ------ 0x420f18 Testing Log Debug • D/plugin ( 366): pixel format [0] unknown has no packing • D/plugin ( 366): pixel format [1] 8888 has packing ARGB [24 8] [0 8] [8 8] [16 8] • D/plugin ( 366): pixel format [2] 565 has packing ARGB [0 0] [11 5] [5 6] [0 5] • D/plugin ( 366): ------ 0x420f18 Testing DOM Access • D/plugin ( 366): ------ 0x420f18 Testing JavaScript Access • E/plugin ( 366): ------ 0x420f18 Invalid Variant type for JS Return: 4,3 • D/plugin ( 366): ------ 0x420f18 PluginType is 3 • D/plugin ( 366): *** 0x420f18 END NPP_New *** • D/plugin ( 366): *** 0x312a10 START NPP_New *** • D/plugin ( 366): ------ 0x312a10 DrawingModel is 1 • D/plugin ( 366): Application data dir is /data/data/com.android.browser/app_plugins • D/plugin ( 366): ------ 0x312a10 PluginType is 6 • D/plugin ( 366): *** 0x312a10 END NPP_New *** • D/plugin ( 366): *** 0x420f18 NPP_SetWindow ***
Android中的浏览器插件开发分析 • 运行测试 • D/dalvikvm( 366): Trying to load lib /data/data/com.android.sampleplugin/lib/libsampleplugin.so 0x43c2e448 • D/dalvikvm( 366): Added shared lib /data/data/com.android.sampleplugin/lib/libsampleplugin.so 0x43c2e448 • D/plugin ( 366): *** 0x312a10 NPP_SetWindow *** • D/plugin ( 366): -------- repeat timer 5 • D/plugin ( 366): -------- latency test: [1937207155] interval 421 expected 50, total 421 expected -1923890058, drift 1923890479 avg 0 • D/plugin ( 366): -------- oneshot timer • D/plugin ( 366): -------- repeat timer 4 • D/plugin ( 366): -------- latency test: [1937207156] interval 473 expected 50, total 894 expected -1923890008, drift 1923890902 avg 0 • E/plugin ( 366): ----0x312a10 Invalid Surface Dimensions (300,150):(120,60) • D/plugin ( 366): -------- repeat timer 3 • D/plugin ( 366): -------- latency test: [1937207157] interval 73 expected 50, total 967 expected -1923889958, drift 1923890925 avg 0 • D/plugin ( 366): -------- repeat timer 2 • D/plugin ( 366): -------- latency test: [1937207158] interval 130 expected 50, total 1097 expected -1923889908, drift 1923891005 avg 0 • D/plugin ( 366): *** 0x420f18 NPP_HandleEvent *** • D/plugin ( 366): ------ 0x420f18 the plugin received an onLoad event • D/plugin ( 366): *** 0x312a10 NPP_HandleEvent *** • D/plugin ( 366): -------- repeat timer 1
Android中的浏览器插件开发分析 • 运行测试 • D/plugin ( 366): -------- latency test: [1937207159] interval 90 expected 50, total 1187 expected -1923889858, drift 1923891045 avg 0 • D/dalvikvm( 54): GC freed 8773 objects / 568952 bytes in 169ms • D/plugin ( 366): *** 0x312a10 NPP_HandleEvent *** • D/plugin ( 366): *** 0x312a10 NPP_HandleEvent *** • D/plugin ( 366): *** 0x312a10 NPP_HandleEvent *** • D/plugin ( 366): *** 0x420f18 NPP_HandleEvent *** • D/plugin ( 366): *** 0x420f18 NPP_HandleEvent *** • D/PowerManagerService( 54): setPowerState: mPowerState=3 newState=7 noChangeLights=false • D/PowerManagerService( 54): oldKeyboardBright=false newKeyboardBright=false • D/PowerManagerService( 54): oldScreenBright=true newScreenBright=true • D/PowerManagerService( 54): oldButtonBright=false newButtonBright=true • D/PowerManagerService( 54): oldScreenOn=true newScreenOn=true • D/PowerManagerService( 54): oldBatteryLow=false newBatteryLow=false • W/KeyCharacterMap( 366): No keyboard for id 0 • W/KeyCharacterMap( 366): Using default keymap: /system/usr/keychars/qwerty.kcm.bin • D/plugin ( 366): *** 0x420f18 NPP_SetWindow *** • D/plugin ( 366): *** 0x420f18 NPP_Destroy *** • D/plugin ( 366): *** 0x312a10 NPP_SetWindow *** • D/plugin ( 366): *** 0x312a10 NPP_Destroy *** • D/plugin ( 366): *** NP_Shutdown ***
Android中的浏览器插件开发分析 插件工作流程 • 浏览器解析页面时,遇到插件的MIME类型,就去检查插件注册表,如果有,就加载插件; • 在插件加载之后,插件先会进行API映射,即把各种调配资源的API映射到NPNetscapeFuncs的结构体指针上,然后作为输入参数调用NP_Initialize() NP_Initialize()只被调用一次,; • 初始化API返回成功后(一个NPPetscapeFuncs结构体指针),插件入口API将被调用,它允许浏览器不必常规地来调用插件端的APIs,这样入口API返回成功后,浏览器的NPPetscapeFuncs结构体将被插件端有效的APIs指针填充(根据适当的内部流程),并将立即按需被调用;
Android中的浏览器插件开发分析 插件工作流程 • 调用NPP_New(),实例化插件,如上面的实例0x312a10和0x420f18;每个实例都会被分配给一个数据块,每个实例根据插件的定义填充参数; • 调用NPP_SetWindow() ,显示插件; • 如果在插件上点击鼠标之类的,就会调用NPP_HandleEvent(); • 关闭此页面,先会调用NPP_SetWindow(),然后调用NPP_Destroy(),释放实例的资源; • 全部实例被 Destroy后,调用NP_ShutDown(),释放全局资源。
Android中的浏览器插件开发分析 插件工作流程
本章小结 本章为Android浏览器扩展,主要介绍开发浏览器扩展插件的方法,先对浏览器插件进行了介绍,然后介绍了BrowserPlugin,最后完成了一个浏览器插件的编译和运行。