【转】Android浏览器插件开发 - Braincol
2011-8-23 15:45:0 Author: www.cnblogs.com(查看原文) 阅读量:5 收藏

最近做android浏览器插件学到一些东西和大家分享:
需要了解的有以下几个方面的知识:
1.插件是什么
2.android浏览器怎样加载插件和创建实例
3浏览器插件和脚本语言的交互
4插件内部的数据流

一 浏览器插件介绍:
   1.1 概述
浏览插件本质是一个功能模块,是浏览器功能的一种扩充。其载体是dll或则so文件。它依附浏览器完成某一特定的功能。插件需要实现浏览器规定的一些函数这些函数叫着NPAPI.正是插件实现了这些函数才可以和浏览器交互。同时浏览器也为插件提供一些函数。在android平台下还有一些专有的函数。他们的函数名字都有约定。插件提供的方法以NPP_打头。浏览器提供的方法是NPN_,android提供的函数是以ANP开头的。
     插件作为一个共享库那么它什么时候被加载。有导出了什么接口让浏览器调用。
     浏览器插件是被浏览器加载的。在android下也是被webkit加载。通常一个共享库被加载都是调用loadlibray函数,然后使用getentrypoint函数得到共享库导出函数的地址。同样插件也是这样被浏览器加载的。既然要调用loadlibrary函数那么我们必须知道共享库在系统中的位置。对于windows,mac,linux系统都可以存放在固定的位置。在linux下还可以通过设置环境变量来设定路径。但是在android系统下不是这样的。在1.5的时候有个固定的路径data/data/com.android.browser/plug-in.但是在2.0以后就没有存到这个路径。而是存放在插件APK包安装目录下的lib文件夹下。比如插件的包名是com.android.plugin,那么共享库的存储路径应该是data/data/com.android.plugin/lib/。对于各种插件路径不一样,那么浏览器是如何找到这些lib的路径,下面将详细介绍。
1.2 android浏览器插件
     对于android浏览器插件,是以apk包形式发布的。并且在该工程中我们有定义一个service。这个service可以响应PLUGIN_ACTION。这个是在AndroidMainefest.xml中设定的。而插件的注册也是通过service完成的。

二、android浏览器加载插件
     2.1 总述
1.1中提到了浏览器加载插件是通过loadlibary方式加载。并且需要知道路径。其实浏览器加载插件总的分为三步:
1.浏览器寻求插件路径,这个是通过插件apk包安装时运行的service来找到的。
2.浏览器获取插件的信息。得到插件的名字,描述和MIME信息并保存到自己的plugindatabase类实例中。
3.浏览器创建插件实例。这个过程创建插件实例,并对插件的内部数据初始化。
下面详细描述这三个过程:
2.2浏览器获取插件的路径。
每次在浏览器启动刷新页面时,便会刷新自己的插件信息库即更新自己的pluginDatabase。然后PluginDataBase的函数refresh便会调用plugin manager的函数得到插件路径。而plugin manager通过pack manager找到所有能够响应PLUGIN-ACTION intent的service。然后通过每一个service信息得到包的名字就可以找到插件了。具体可以参考frameworks/base/core/java/android/webkit/ PluginManager.java。

packageManager 

图1描述了浏览器得到插件路径的流程。详细可以参考源码。路径:/frameworks/base/core/java/android/webkit
external/webkit/WebCore/plugins
external/webkit/WebKit/android/jni

     2.3 浏览器获取插件信息
     得到插件路径后,我们可以得到插件导出的函数地址了,首先我们了解一下插件导出的函数和他们的功能。

plugin.so

   图2显示了一个插件共享库导出的函数。下面详细说明每个函数的功能。
   在浏览器调用refresh后将会调用NP_GetValue得到插件的名字和描述信息。然后调用NP_GetMIMEDescription得到插件的MIME类型、支持文件的扩展名和描述。将这些信息保存到pluginDatabase中。NP_Shutdown是在销毁pluginview时会调用,作用释放插件的资源。关于NP_Initialize函数是在创建插件实例时才会调用。具体过程如图3所示。

progress

在找不到MIMEType时浏览器会根据数据文件的扩展名来匹配插件。
2.4浏览器创建插件实例:
NP_Initialize函数比较重要。它是浏览器和插件进行交互的关键。他的功能主要有三个:
1.得到浏览器定义的NPN_函数地址。
2.将插件定义的NPP_函数地址返回给浏览器
3.得到Android提供的一些ANP_函数。

下面列出了这些函数:
//NPP函数,插件和浏览器交互的主要函数

NPP

上面的函数都有一个NPP instance参数,其实它是一个指向pluginview成员变量的指针。而该成员变量又有两个指针pdata和ndata。pdata指向我们的插件NPOject对象。在NPP_New中可以看到这个附值。ndata存地则是pluginview自己的this指针
//android提供的函数

ANP

上面这些函数的初始化是通过NPN_Getvalue得到的。

//插件提供的另外一些函数,这些函数主要是完成插件和javasript的交互使用。

plugin-js

在调用完NP_Initialize函数后浏览器就知道NPP_New函数的地址了。这时浏览器调用该函数创建浏览器实例。该函数是在pluginview中被调用的。
在NPP_New中我们创建插件的实例NPObject。NPObject的创建是调用浏览器的NPN_create函数创建的。在该create函数中又判断NPObject自己提供create方法没(creat方法的地址通过NPN_create第二个参数传入的),如果没有那么浏览器自己调用malloc创建实例。并且将插件提供的NPObject函数地址(上面列出的static函数)保存在NPObject对象里面。到此插件的实例就创建完了。

三、浏览器插件和脚本语言的交互

浏览器提供了插件和javascript交互的机制。
首先,看一下java script如何调用插件的方法的。
浏览器首先会调用NPError NPP_GetValue(NPP instance, NPPVariable variable, void* value)取得NPObject对象的地址。Variable参数为NPPVpluginScriptableNPObject。在取得该对象后浏览器就可以调用插件提供的NPClass函数。最主要的函数有下面几个:
pluginHasMethod :询问插件是否支持某一js方法。
pluginHasProperty :询问插件是否具有某一属性
pluginInvoke :     当插件支持某一方法时,浏览器将会调用该函数执行插件为js提供的这一方法。那么对于提供的很多方法插件如何在该函数内区分。
我们来分析一下该函数:
static bool pluginInvoke(NPObject *obj, NPIdentifier name, const NPVariant *args, uint32_t argCount, NPVariant *result);
obj是插件里的NPObject对象地址。
Name表示插件提供方法的名字,通过对比这个参数来区分插件提供的不同方法。
Args和argcount分别表示js传来的参数地址和参数个数。
Result得到的结果。

下来我们看一下插件是如何调用js提供的方法:
Js可以通过2种方式为插件设置回调函数。伪代码如下:
<script language=javascript>
Plugin.Onfun = fun;//方式一  通过设置插件属性传入回调函数地址
Plugin.Onfun(fun);//方式二   通过调用插件函数传入回调函数地址
Function fun(){}
</script>

在插件内部,当js函数地址传到插件时,浏览器把它封装为一个NPObject对象,里面存有函数地址。
方式一: 在插件内部,浏览器会调用pluginHasproperty确认插件是否有该属性。如果有然后浏览器调用pluginSetproperty函数。pluginSetProperty(NPObject *obj, NPIdentifier name, const NPVariant *variant)的第二个参数判断是哪个属性,第三个参数就是NPObject对象地址。
方式二:在插件内部,浏览器会调用pluginHasmethod确定是否支持该方法。然后调用pluginInvoke,这里面的args参数包含了回调函数NPObject地址。
js设置完回调函数后,插件就可以调用该函数了。需要使用NPN_InvokeDefault,示例代码如下:
bool bret = gBrowser->invokeDefault(npp, callbackNPObject, &pV, 1, &result);

另外,插件也可以直接调用js中的函数。在插件内部调用浏览器的getUrl函数。具体格式如下:
gBrowser->geturl(inst(), “javascript:function()”, "_self");
如果想传入整数参数,上面函数第二个参数应写成: “javascript:function(“+num+”)”。
如果传入字符串参数,上面函数第二个参数为: “javascript:function(/’“+”string”+”/’)”。如果字符串含有中文,需要进行url encode。
四、插件数据流

插件可以通过浏览器向服务器请求数据,同时也可以向服务器发送数据。
如果插件需要向服务器请求数据时可以调用浏览器函数NPN_Geturl向服务器发送请求。里面的target参数设置为NULL数据就可以传给本页面的插件。请求成功浏览器会调用插件的NPP_newstream函数,
通过NPP_Newstream创建流时,将传递一个流的模式参数,plug-in在它返回时设置这个参数,缺省设置是NP_Normal;通过NPP_DestroyStream删除流.Plug-in也可以调用NPN_DestroyStream删除流.这三种模式分别如下:
正常模式.当参数设置为NP_Normal时采用该模式,当有数据可发送时Netscape就把数据发送给plug-in,这些数据可能是以非正常顺序到达。浏览器通过调用一系列的NPP_WriteReady和NPP_Write来发送数据。浏览器通过len这个参数告诉plug-in它将发送多少数据,Netscape调用NPP_WriteReady来确定plug-in每次准备接收多少字节的数据,再调用NPP_Write发送数据.此种模式的效率较高.
随机存取模式.若调用NPP_NewStream时将其中的布尔型参数Seekable设为真时,就采用此种模式.此时,流中的数据先由plug-in调用NPN_RequestRead加以指明所要获取的数据的范围,然后浏览器调用NPP_WriteReady和NPP_Write把数据传送给plug-in.这种模式需要远程服务器的支持或浏览器先将流数据存到本地的临时文件中.用这种模式时,用户可以从服务器的数据文件中任意读取自己想要的记录,就如同从本地硬盘上读取一个记录一样.
文件模式.把参数设置为NP_AsFile即可.浏览器先将整个Url数据存到一个本地文件中,然后通过NPP_StreamAsFile将文件名传给plug-in。Plug-in可以通过文件操作获得所要数据.

五,总结
   对于浏览器插件的开发可以参考源码的实例,development/samples/browseplugin实例。相关知识可以参考https://developer.mozilla.org/en/Gecko_Plugin_API_Reference

转载自:http://blog.csdn.net/ownerwu/article/details/6429072


文章来源: https://www.cnblogs.com/hibraincol/archive/2011/08/23/2150629.html
如有侵权请联系:admin#unsafe.sh