32
第第 Android 第第第第第

第十一章 Android 浏览器扩展

  • Upload
    astra

  • View
    187

  • Download
    0

Embed Size (px)

DESCRIPTION

第十一章 Android 浏览器扩展. 本章主要内容. NPAPI 简介. Netscape Plugin Application Programming Interface 是一个被许多浏览器遵循和采用的跨平台的插件框架。 NPAPI 的接口分为两组:浏览器侧的 NPN 接口和插件侧的 NPP 接口。 浏览器插件的核心,就是一个实现了 NPP 接口,并使用浏览器提供的 NPN APIs 进行对外操作的动态库。. NPAPI 简介. - PowerPoint PPT Presentation

Citation preview

Page 1: 第十一章  Android 浏览器扩展

第十一章 Android 浏览器扩展

Page 2: 第十一章  Android 浏览器扩展

本章主要内容

NPAPI 简介

Android 中浏览器插件开发分析

Page 3: 第十一章  Android 浏览器扩展

NPAPI 简介

Netscape Plugin Application Programming Interface 是一个被许多浏览器遵循和采用的跨平台的插件框架。

NPAPI 的接口分为两组:浏览器侧的 NPN 接口和插件侧的 NPP 接口。

浏览器插件的核心,就是一个实现了 NPP 接口,并使用浏览器提供的 NPN APIs 进行对外操作的动态库。

Page 4: 第十一章  Android 浏览器扩展

NPAPI 简介

Android 浏览器插件特有的结构:插件的 Java 层。这样,浏览器插件就可以作为一个 Android 应用,通过常规途径安装到 Android 设备中。( Android 的所有应用都必须通过 Java 部分实现安装)

Page 5: 第十一章  Android 浏览器扩展

Android 中的浏览器插件开发分析

BrowserPlugin

Android 的源码目录下提供了 Plugin 的范例: development/samples/BrowserPlugin ;

通过这个版本的例子编译生成的是完整的 apk 安装包,可以在模拟器或者真机上安装测试。

Page 6: 第十一章  Android 浏览器扩展

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 文件 。

Page 7: 第十一章  Android 浏览器扩展

Android 中的浏览器插件开发分析

jni 目录静态结构

<<i nterface>>NPNetscapeFuncs

main

<<i nterface>>SubPlugin

<<i nterface>>SurfaceSubPlugin

PaintPlugin

FormPlugin

BackgroundPlugin

AudioPlugin Bal lAnimation

Page 8: 第十一章  Android 浏览器扩展

Android 中的浏览器插件开发分析

jni 目录静态结构

Page 9: 第十一章  Android 浏览器扩展

res 目录,和一般的 android 工程一样,存放资源的目录。 src 目录下有实现了一个 android 的 service 类(其他大部分插件是实

现 activity 类),并为插件提供绘制接口;有SamplePluginStub.java 、 SamplePlugin.java: 实现服务接口、 Cube.java 、 CubeRenderer.java 文件。

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

Android.mk ,编译配置文件。

Android 中的浏览器插件开发分析

BrowserPlugin 结构

Page 10: 第十一章  Android 浏览器扩展

Android 中的浏览器插件开发分析

NPP APIs

NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16 mode, int16 argc, char* argn[], char* argv[], NPSavedData* saved);

新建一个实例,浏览器每创建一个 plugin 的实例就会调用一次这个函数。通过 NPN_SetValue 告知浏览器 plugin 对象的一些特性,其中包括了 plugin 对象能处理的事件(触控事件和按键事件),以及 plugin 的渲染模式( bitmap 模式或surface 模式)。

Page 11: 第十一章  Android 浏览器扩展

Android 中的浏览器插件开发分析

NPP APIs

NPError NPP_Destroy(NPP instance, NPSavedData** save); 当浏览器需要销毁一个 plugin 实例的时候调用,要在这里完成对

应实例的资源释放。 NPError NPP_SetWindow(NPP instance, NPWindow* window); 浏览器通过该函数告知 plugin 对象其窗口参数,主要就是的

plugin 对象所占画面的大小。

Page 12: 第十一章  Android 浏览器扩展

Android 中的浏览器插件开发分析

NPP APIs

NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16* stype);

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

Page 13: 第十一章  Android 浏览器扩展

Android 中的浏览器插件开发分析

NPP APIs

NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason);

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

int32 NPP_WriteReady(NPP instance, NPStream* stream); 浏览器在给 plugin 对象传输流数据前,会先调用这一函数询

问 plugin 能接收的数据长度。

Page 14: 第十一章  Android 浏览器扩展

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 流的信息。

Page 15: 第十一章  Android 浏览器扩展

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 结构体的定义。

Page 16: 第十一章  Android 浏览器扩展

Android 中的浏览器插件开发分析

NPP APIs

void NPP_URLNotify(NPP instance, const char* URL, NPReason reason, void* notifyData);

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

Page 17: 第十一章  Android 浏览器扩展

Android 中的浏览器插件开发分析

NPP APIs

NPError NPP_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 。 NPError NPP_SetValue(NPP instance, NPNVariable variable, void

*value);

Page 18: 第十一章  Android 浏览器扩展

Android 中的浏览器插件开发分析

NPP APIs

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 。

Page 19: 第十一章  Android 浏览器扩展

Android 中的浏览器插件开发分析

ANPInterface

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

其实 ANPInterface 提供的接口,大多来自 webkit 的一些底层库( external/webkit/WebKit/android/plugins )

BrowserPlugin 中的 ANPInterface 列表如下:

Page 20: 第十一章  Android 浏览器扩展

Android 中的浏览器插件开发分析

ANPInterface

ANPAudioTrackInterfaceV0 gSoundI; ANPBitmapInterfaceV0 gBitmapI; ANPCanvasInterfaceV0 gCanvasI; ANPLogInterfaceV0 gLogI; ANPPaintInterfaceV0 gPaintI; ANPPathInterfaceV0 gPathI; ANPSurfaceInterfaceV0 gSurfaceI; ANPSystemInterfaceV0 gSystemI; ANPTypefaceInterfaceV0 gTypefaceI; ANPWindowInterfaceV0 gWindowI;

Page 21: 第十一章  Android 浏览器扩展

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侧的交互。

Page 22: 第十一章  Android 浏览器扩展

Android 中的浏览器插件开发分析

NP_Shutdown

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

Page 23: 第十一章  Android 浏览器扩展

Android 中的浏览器插件开发分析

编译 BrowserPlugin

修改 jni/main.cpp 文件之后编译(主要是增加 LOGCAT 调试信息),以方便后面分析插件加载流程。

进入源码根目录下

运行 make SampleBrowserPlugin

Page 24: 第十一章  Android 浏览器扩展

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>

Page 25: 第十一章  Android 浏览器扩展

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 ***

Page 26: 第十一章  Android 浏览器扩展

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

Page 27: 第十一章  Android 浏览器扩展

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 ***

Page 28: 第十一章  Android 浏览器扩展

Android 中的浏览器插件开发分析

插件工作流程

浏览器解析页面时,遇到插件的 MIME 类型,就去检查插件注册表,如果有,就加载插件;

在插件加载之后,插件先会进行 API映射,即把各种调配资源的API映射到 NPNetscapeFuncs 的结构体指针上,然后作为输入参数调用 NP_Initialize() NP_Initialize()只被调用一次,;

初始化 API 返回成功后(一个 NPPetscapeFuncs 结构体指针),插件入口 API 将被调用,它允许浏览器不必常规地来调用插件端的APIs ,这样入口 API 返回成功后,浏览器的 NPPetscapeFuncs 结构体将被插件端有效的 APIs指针填充(根据适当的内部流程),并将立即按需被调用;

Page 29: 第十一章  Android 浏览器扩展

调用 NPP_New() ,实例化插件,如上面的实例 0x312a10 和0x420f18 ;每个实例都会被分配给一个数据块,每个实例根据插件的定义填充参数;

调用 NPP_SetWindow() ,显示插件; 如果在插件上点击鼠标之类的,就会调用 NPP_HandleEvent() ; 关闭此页面,先会调用 NPP_SetWindow() ,然后调用

NPP_Destroy() ,释放实例的资源; 全部实例被 Destroy 后,调用 NP_ShutDown() ,释放全局资源。

Android 中的浏览器插件开发分析

插件工作流程

Page 30: 第十一章  Android 浏览器扩展

Android 中的浏览器插件开发分析

插件工作流程加载页面

根据MI ME类型查找注册的插件

找到插件

NP_Initialize

Y

插件已加载 N

NPP_New

Y

NPP_SetWindow

NPP_HandleEvent

关闭页面

NPP_SetWindow

NPP_Destroy

是否还有其他插件实例

Y

NP_ShutDown

End

Y

N

N

错误处理

Page 31: 第十一章  Android 浏览器扩展

本章小结

本章为 Android 浏览器扩展,主要介绍开发浏览器扩展插件的方法,先对浏览器插件进行了介绍,然后介绍了 BrowserPlugin ,最后完成了一个浏览器插件的编译和运行。

Page 32: 第十一章  Android 浏览器扩展

THE END