phoengap源码解析——插件机制,java和js代码互调用详解
2012-01-28 03:46 onm 阅读(367) 评论(0) 编辑 收藏 举报看了眼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中有如下一段可以说明:
1 2 3 4 5 | <!-- 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相应代码为:
1 2 3 4 5 6 7 8 9 10 | 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端浏览器代码接受到这个,然后在响应的处理函数中根据传过来的参数,实现了一些特别的逻辑。可以从这个方法的注释上看出一二。
1 2 3 4 5 6 7 8 9 10 | /** * 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) { ... } |
方法里面的代码就如刚所说会根据传过来的参数,做相应处理,它会先判断是不是本地网页的请求,如果是,然后分几种情况。
1 2 3 4 5 6 7 8 9 | // 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文件中
1 2 3 4 | /** * 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是回调服务器的代码所在处。从类的注释中可以看到。
1 2 3 | 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的相关代码。
1 2 3 4 5 6 7 8 9 | /** * 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代码,进行回调。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | /** * 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源码的注释是很不错的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述
2011-01-28 Mail,Memcache Python API——Google App Engine Services简介
2011-01-28 autoping——Micolog插件