Android浏览器插件开发
最近做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。
图1描述了浏览器得到插件路径的流程。详细可以参考源码。路径:/frameworks/base/core/java/android/webkit
external/webkit/WebCore/plugins
external/webkit/WebKit/android/jni
2.3 浏览器获取插件信息
得到插件路径后,我们可以得到插件导出的函数地址了,首先我们了解一下插件导出的函数和他们的功能。
图2显示了一个插件共享库导出的函数。下面详细说明每个函数的功能。
在浏览器调用refresh后将会调用NP_GetValue得到插件的名字和描述信息。然后调用NP_GetMIMEDescription得到插件的MIME类型、支持文件的扩展名和描述。将这些信息保存到pluginDatabase中。NP_Shutdown是在销毁pluginview时会调用,作用释放插件的资源。关于NP_Initialize函数是在创建插件实例时才会调用。具体过程如图3所示。
在找不到MIMEType时浏览器会根据数据文件的扩展名来匹配插件。
2.4浏览器创建插件实例:
NP_Initialize函数比较重要。它是浏览器和插件进行交互的关键。他的功能主要有三个:
1.得到浏览器定义的NPN_函数地址。
2.将插件定义的NPP_函数地址返回给浏览器
3.得到Android提供的一些ANP_函数。
下面列出了这些函数:
//NPP函数,插件和浏览器交互的主要函数
上面的函数都有一个NPP instance参数,其实它是一个指向pluginview成员变量的指针。而该成员变量又有两个指针pdata和ndata。pdata指向我们的插件NPOject对象。在NPP_New中可以看到这个附值。ndata存地则是pluginview自己的this指针
//android提供的函数
上面这些函数的初始化是通过NPN_Getvalue得到的。
//插件提供的另外一些函数,这些函数主要是完成插件和javasript的交互使用。
在调用完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
补充:移动开发 , Android ,