您的位置:首页 > Web前端 > JavaScript

phoengap源码解析——插件机制,java和js代码互调用详解

2012-01-28 03:46 1156 查看
看了眼phonegap的android实现的源码,其实还是挺简单的。出乎意料的是,我原以为js这套api的封装是使用addJavascriptInterface这个接口完成的,但是它是用了一种奇异的方法来实现的。我们一步一步慢慢说。

http://docs.phonegap.com/en/1.3.0/index.html这个链接是phonegap的js api,可以看到api是分模块的,有Accelerometer,Camera,Capture,Compass,Connection,Contacts,Device,File,Geolocation,Media,Notification,Storage,Events,而每一个模块恰好是phonegap的一个plugin。而每一个plugin恰好大体对应一个java文件和一个js文件。在实际用phonegap开发的时候,我们引用的phonegap.js是经过编译整合后的文件,源码的framework/build.xml中有如下一段可以说明:

<!-- Create uncompressed JS file -->
<concat destfile="assets/www/phonegap-${version}.js">
<fileset dir="assets/js" includes="phonegap.js.base" />
<fileset dir="assets/js" includes="*.js" />
</concat>

插件机制对应的java代码,主要在com.phonegap.api这个包里,所有的api对应模块的java实现都扩展了Plugin类,并且在src/com/phonegap/api/PluginManager.java中有public String exec(final String service, final String action, final String callbackId, final String jsonArgs, final boolean async);方法,此方法里面调用plugin.execute方法,相应的plugin插件实现了execute方法,进行具体操作。对应js代码,插件机制以及公有基础的东西主要phonegap.js.base文件,每个单独的模块文件实现具体的操作。js代码里也有相应定义,

/**

* Execute a PhoneGap command. It is up to the native side whether this action is synch or async.

* The native side can return:

* Synchronous: PluginResult object as a JSON string

* Asynchrounous: Empty string ""

* If async, the native side will PhoneGap.callbackSuccess or PhoneGap.callbackError,

* depending upon the result of the action.

*/

public String exec(final String service, final String action, final String callbackId, final String jsonArgs, final boolean async);

可以看到api操作还提供了同步异步两种模式。这里的一些东西到这里可能还不是很明朗,稍后了解了java和js互掉的原理后就清楚了。

但是这里,特别的对于Events有些特别,java与js交互上是不同于其它模块的方法且相对简单的,所以先说这个。在src/com/phonegap/DroidGap.java文件中可以看到,所有js的回调事件都是通过loadUrl然后执行一段javascript脚本来实现的,如

生命周期回调:this.appView.loadUrl("javascript:try{PhoneGap.fireDocumentEvent('resume');}catch(e){};");

按钮响应:this.appView.loadUrl("javascript:PhoneGap.fireDocumentEvent('backbutton');");

assets/js/phonegap.js.base相应代码为:

PhoneGap.fireDocumentEvent = function(type, data) {
var e = document.createEvent('Events');
e.initEvent(type);
if (data) {
for (var i in data) {
e[i] = data[i];
}
}
document.dispatchEvent(e);
};

好了,开始最重要的部分了。phonegap使用的java与js互相调用的原理。phonegap实现的模型刚也说了,有同步和异步两种。js实现的api,所以是js先会调用java代码,然后再返回给js。对于同步的而言,就是js调用java,然后java返回一个结果作为返回值。对于异步的而言,可能js掉了很多java代码,但是立即返回,然后java代码执行结束后再回调js代码,这里就涉及到js调java,然后java再调用js。

对于js调用java:

js调用java的入口是通过在js中调用prompt方法,这很奇怪吧,这个方法本来是让浏览器弹出个输入框的。我当初找了好久也没发现phonegap到底怎么搞得的让js调用java的代码,后来看到一会觉得该是这个方法,但是这是一个浏览器的客户端自己的东西,而且怪异的是浏览器并没有弹出输入框,后来终于发现。

在DroidGap.java中有个hack,重载了WebviewClient的onJsPrompt方法,然后执行了自己的逻辑。 也就是js调用prompt的时候,java端浏览器代码接受到这个,然后在响应的处理函数中根据传过来的参数,实现了一些特别的逻辑。可以从这个方法的注释上看出一二。

/**
* Tell the client to display a prompt dialog to the user.
* If the client returns true, WebView will assume that the client will handle the prompt dialog
* and call the appropriate JsPromptResult method.
* Since we are hacking prompts for our own purposes, we should not be using them for this
* purpose, perhaps we should hack console.log to do this instead!
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
...
}

方法里面的代码就如刚所说会根据传过来的参数,做相应处理,它会先判断是不是本地网页的请求,如果是,然后分几种情况。

// Calling PluginManager.exec() to call a native service using
prompt(this.stringify(args), "gap:"+this.stringify([service, action, callbackId, true]));
gap:
// Polling for JavaScript messages
gap_poll:
// Calling into CallbackServer
gap_callbackServer:
// PhoneGap JS has initialized, so show webview(This solves white flash seen when rendering HTML)
gap_init:

分别是,如果是prompt传过来的是gap:这样开头的字符串,那么就执行相应的java调用,Calling PluginManager.exec() to call a native service using prompt(this.stringify(args), "gap:"+this.stringify([service, action, callbackId, true]))。gap_poll:和gap_callbackServer:是稍后要说到的java回调js使用的,gap_init:初始化处理相关的代码。如果其它情况,则构造一个Android的AlertDialog显示。

然后phonegap就是这样,通过设置setWebChromeClient和setWebViewClient,重载了一些实现,控制了浏览器行为,实现了自己的很多逻辑。

对于java回调js:

phonegap实现了一个回调服务器,服务器就是负责回调js代码的,服务器有一个js代码的队列,在src/com/phonegap/CallbackServer.java文件中

/**
* The list of JavaScript statements to be sent to JavaScript.
*/
private LinkedList javascript;

服务器保存要回调的js的代码,供js客户端取回,这里java端是服务器端,js端是客户端,服务器端不可能请求客户端做啥,是b/s模型,所以phonegap实现了两种服务模型,一种是轮询,一种是XHR异步回调,也就是Ajax的模型。src/com/phonegap/CallbackServer.java是回调服务器的代码所在处。从类的注释中可以看到。

This class provides a way for Java to run JavaScript in the web page that has loaded PhoneGap.
The CallbackServer class implements an XHR server and a polling server with a list of JavaScript statements
that are to be executed on the web page.

CallbackServer提供的这两种模型,一种是XHR,一种是轮询,轮询很简单了,callbackserver服务器端,有一个保存回调js的列表,前面所说,然后每隔一段时间客户端的js会询问一次服务器,是否有需要回调的js,如果有则调用,然后每隔一段时再查询一次服务器。而基于XHR的,其实这个就是ajax用的机制了,js发起一个异步请求,然后服务器会在返回数据之前保持住这个连接,当返回数据就位后,服务器给请求客户端返回数据,然后关闭连接。然后客户端接受并且处理。

刚说了服务器端的代码实现,现在来看一下客户端js的相关代码。

/**
* Internal function that uses XHR to call into PhoneGap Java code and retrieve any JavaScript
* code that needs to be run. This is used for callbacks from Java to JavaScript.
*/
PhoneGap.JSCallback = function() {
...
xmlhttp.open("GET", "http://127.0.0.1:"+PhoneGap.JSCallbackPort+"/"+PhoneGap.JSCallbackToken , true);
xmlhttp.send();
}

这个是XHR模型的代码,客户端js使用xhr请求服务器来获取js代码,进行回调。

/**
* Internal function that uses polling to call into PhoneGap Java code and retrieve
* any JavaScript code that needs to be run.  This is used for callbacks from Java to JavaScript.
*/
PhoneGap.JSCallbackPolling = function() {
...
var msg = prompt("", "gap_poll:");
if (msg) {
setTimeout(function() {
try {
var t = eval(""+msg);
}
catch (e) {
console.log("JSCallbackPolling: Message from Server: " + msg);
console.log("JSCallbackPolling Error: "+e);
}
}, 1);
setTimeout(PhoneGap.JSCallbackPolling, 1);
}
else {
setTimeout(PhoneGap.JSCallbackPolling, PhoneGap.JSCallbackPollingPeriod);
}
}

这个是轮询方式的,可以看到客户端每隔PhoneGap.JSCallbackPollingPeriod段时间,就请求一次服务器(通过prompt("", "gap_poll:");)。

至此js和java互调用,phonegap的plugin插件机制和api的实现原理就清楚了。另外phonegap源码的注释是很不错的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐