传送门:
一、 Javascript 的源码结构
提醒一下大家 ,PhoneGap 的作者已经将 PhoneGap 的源码委托给了 Apache 基金会。 PhoneGap 的开源版本称为 cordova 。
PhoneGap 之于 Cordova ,正如 OpenJDK 之于 JDK 。两者基本上是差不多的。
cordova.android.js 是一个 build 版本。本人对其做了反 build 工作 ,cordova.android.js 展开的源码目录结构便如上所示。
从命名上其实已经可以看出 ,lib/android 属于 android 平台的专用库。其余平台基本上是 lib/windowsphone 或者 lib/ios 。而除 lib 下的 exec.js 和 platform.js 这两个是固定的。但是不同平台版本其实现是不一样的。除 lib 包外,其余所有的 js 文件都是跨平台通用的。 ( 即使有差异应该也不大 )
简单介绍一下各个目录和一些关键组件 :
cordova.js:拦截DOM,Window事件,加入自定义cordova事件,管理回调Javascript。 scripts/require.js:PhoneGap中模块化机制的基础框架。简单但是也不简单! scripts/bootstrap.js:负责cordova的启动。 common/channel.js:PhoneGap中实现事件监听的基础。 common/builder.js:具备定制化构造模块的能力。 common/plugin:如名字所示。这里放置所有平台通用的插件接口。 lib/exec.js:于上一篇解析中提到。是Javascript调用Native的入口。 lib/platform.js:与平台实现有关的初始化。 lib/android/plugin:与android平台紧密相关的插件。
二、 浅析 PhoneGap 中的模块化机制
也许是因为本人见过的 Javascript 代码太少,见到 PhoneGap 的模块化机制后便觉得非常的有趣和前卫。 Pascal 的作者沃斯曾写过一门叫做 module 的语言,其在语言级别做了模块化机制。我不知道 PhoneGap 模块化的思路是否也受 ; 此影响。
废话不多说了,从 require.js 开始看起吧。它是模块化的基础。
var require, define; (function () { var modules = {}; function build(module) { var factory = module.factory; module.exports = {}; delete module.factory; factory(require, module.exports, module); return module.exports; } require = function (id) { if (!modules[id]) { throw "module " + id + " not found"; } return modules[id].factory ? build(modules[id]) : modules[id].exports; }; define = function (id, factory) { if (modules[id]) { throw "module " + id + " already defined"; } modules[id] = { id: id, factory: factory }; }; define.remove = function (id) { delete modules[id]; }; })(); //Export for use in node if (typeof module === "object" && typeof require === "function") { module.exports.require = require; module.exports.define = define; }
代码行数的确非常短。其定义了 require 和 define 两个函数。首先 define 函数用于声明一个模块。其中 id 表示模块名称,这必须是唯一的,而 factory 便是构造模块的工厂方法。 require 函数使用懒加载的方式获得已 define 过的对应 id 的模块。
来看一个使用其的简单示例吧 :
define("cordova/plugin/android/app", function(require, exports, module) { var exec = require('cordova/exec'); module.exports = { /** * Clear the resource cache. */ clearCache:function() { exec(null, null, "App", "clearCache", []); }, /** * Load the url into the webview or into new browser instance. * * @param url The URL to load * @param props Properties that can be passed in to the activity: * wait: int => wait msec before loading URL * loadingDialog: "Title,Message" => display a native loading dialog * loadUrlTimeoutValue: int => time in msec to wait before triggering a timeout error * clearHistory: boolean => clear webview history (default=false) * openExternal: boolean => open in a new browser (default=false) * * Example: * navigator.app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000}); */ loadUrl:function(url, props) { exec(null, null, "App", "loadUrl", [url, props]); }, /** * Cancel loadUrl that is waiting to be loaded. */ cancelLoadUrl:function() { exec(null, null, "App", "cancelLoadUrl", []); }, /** * Clear web history in this web view. * Instead of BACK button loading the previous web page, it will exit the app. */ clearHistory:function() { exec(null, null, "App", "clearHistory", []); }, /** * Go to previous page displayed. * This is the same as pressing the backbutton on Android device. */ backHistory:function() { exec(null, null, "App", "backHistory", []); }, /** * Override the default behavior of the Android back button. * If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired. * * Note: The user should not have to call this method. Instead, when the user * registers for the "backbutton" event, this is automatically done. * * @param override T=override, F=cancel override */ overrideBackbutton:function(override) { exec(null, null, "App", "overrideBackbutton", [override]); }, /** * Exit and terminate the application. */ exitApp:function() { return exec(null, null, "App", "exitApp", []); } }; });
这是 phonegap 模块化编程的典型写法。首先在头部定义依赖的模块组件 , 再次通过设置 module.exports 向外部暴露出对应的方法。
由于 Javascript 中在语法级别没有私有访问符。因此往往解决之道是: a. 模仿 C 风格,命名使用 _ 开头的一律表示是私有 ;b. 新建一个 private 对象 , 将其私有方法放置其中 , 起到命名空间的作用 ;c. 将私有部分用 function{} 套住 , 用 return 返回公开部分。这种做法充分发挥了 javascript 闭包的优势,但是可读性比较差。
phoneGap 的 module 机制使用了第三种做法。但是通过将构造和依赖分离开来,使得可读性大大增加。代码清晰好懂,依赖性也一目了然。
也许有朋友要问。这种模块机制碰到循环依赖的情况怎么办?例如 A 在 define 阶段 requireB , B 在 define 阶段又 requireA 。很遗憾,这种循环依赖的情况 依照 phoneGap 的模块化思路是无法实现的。因此对于关键组件的编制 ,phoneGap 总会小心翼翼地处理其依赖顺序。
通过阅读源码,发现大致的模块依赖顺序是这样子的 ( 被依赖模块到依赖模块 ):utils.js->channel.js|builder.js->cordova.js->exec.js|polling.js->callback.js->platform.js->bootstrap.js 。
三、 PhoneGap 中的事件处理机制
common/channel.js 是 PhoneGap 事件处理机制的基础。每个事件类型,都会包装成一个 Channel 对象。
既然是事件,那么就得支持基础的观察者模式吧? Channel 的 prototype 定义了事件的一些关键方法。 subscribe 用于注册一个监听器,并给监听器一个 guid 。 guid 类似 cordova.js 中的 callbackId ,只是一个流水标识。但 Channel 中的 guid 稍有些不同,指定确切的 guid 可以对监听器做覆盖操作。
utils.close 是个很有趣的方法。 Javascript 中的调用不当引起 this 不对,这是新手常见的错误。常见的做法会通过封装 apply 做 delegate 。而 close 这个方法是绝了 , 它通过闭包包装了一个指向确定 this ,调用确定 function, 使用确定实参的 final 函数。不管在什么样的环境下调用 , 这个方法总能正确执行。
subscribeOnce 类似于 YUI 或者 jquery 中的 one 。只会收到一次监听。 ( 若事件已经触发过,则在注册阶段立即回调监听 )
unsubscribe 和 fire 分别用来注销监听器和触发事件。触发事件将会引起监听器的广播操作。可选的 fireArgs 用于保证 subscribeOnce 在事件已触发的情况下能获得正确的广播参数。
Channel 本身还有一个监听注册 / 注销的事件拦截。分别是 onSubscribe 和 onUnSubscribe 。在 common\plugin\battery.js 中,我们可以看到。 battery.js 便是利用这个注册监听回调,来对 Plugin 服务做懒加载和卸载工作。
作为模块暴露公有部分的 channel 对象比较有意思。 join 这个工具方法类似 subscribeOnce, 它的第二个参数是个 Channel 数组。当且仅当所有的 Channel 事件都被 fire 后 ,join 的监听才会被回调。这个方法还是挺有用的
create 是个构造工厂方法。新构造的 Channel 事件会被放置在 channel 对象中。使用上会方便点。在 channel.create('onCordovaReady'); 后 , 便可以便捷的通过 channel[‘onCordovaReady’] 来方便的访问对应类型的 Channel 对象了。
deviceReadyMap,deviceReadyArray,waitForInitialization,initializeComplete 这四者紧密相关。它们决定了 onDeviceReady 事件在何时被触发。于 common/bootstrap.js 中我们看到下面一段代码。
channel.join(function() { channel.onDeviceReady.fire(); }, channel.deviceReadyChannelsArray);
waitForInitialization 用于添加 onDeviceReady 的等待 Channel 事件。 initializeComplete 用于触发指定的等待 Channel 事件。如果想要增加 onDeviceReady 的条件,我们只需要在 onCordovaReady 之前添加 waitForInitialization 即可。事实上,在 lib/android/plugin/storage.js 中我们便可以看到一个绝佳的例子。 cupcakeStorage 利用本地 Plugin 为不支持 localStorage API 的 WebView 提供了一个备选方案。在本地建立好备用的 sqlite 数据库后 ,cupcakeStorage 的等待时间便结束完毕。
四、 启动与 PhoneGap 自定义事件
首先上图。
上图为本人整理的启动事件序列,待会儿大家便能从源码中看到了。
待续。。。。
相关推荐
phonegap,cordova2.9源代码,以及自带的示例程序
IONIC+PhoneGap项目源码,爱下不下
phonegap-2.7.0源码 加上对应版本的jar包和js 如果需要别的版本的资源,请留言联系
Phonegap 源码 2.9 最新. 全部包,包括ios android 等等
PhoneGAP实例源码,含汇率换算器、万圣节摇摇乐及指南针实例
Android PhoneGap开发简结
android PhoneGap websocket 插件
android+Phonegap开发环境搭建android+Phonegap开发环境搭建android+Phonegap开发环境搭建android+Phonegap开发环境搭建
该源码是使用phonegap加jquery mobile开发基于html5 css3的应用的项目例子。可以很好的来学校jquery mobile。
4.复制phonegap中android目录下的xml文件夹到 /res下 5.android默认的项目是针对native app的,所以要用web app(cordova)的话,必须修改它的引用文件。 5.1引用加载cordova-1.7.0.jar,这时候可以按F5刷新工程。 ...
Android PhoneGap简析
phonegap的API提供 震动 响铃 调用原生弹窗功能. 此篇文章以cordova 3.3版本编写 据我总结核心步骤: 创建工程 ; 下载插件 ;编译工程; 调用插件; 按照如下步骤就能生产出代码: 打开cmd 控制台 1 使用...
Android Phonegap+HTML5+CSS3+jQuer简单界面模板示例及源码.zip源码资源下载Android Phonegap+HTML5+CSS3+jQuer简单界面模板示例及源码.zip源码资源下载
PhoneGap Android is an Android application library that allows for PhoneGap-based projects to be built for the Android Platform. PhoneGap-based applications are, at the core, an application written in...
■ 用PhoneGap和它的NFC插件写Android上的NFC应用程序用例 ■ 深入了解NDEF结构:测试已有的写标签程序,然后写自己的标签程序 ■ 用PhoneGap事件监听器来监听和过滤NDEF消息 ■ 写一个完整的Android应用程序来控制...
android phonegap
在Android中使用PhoneGap去访问Html,方便开发
一个ionic+phonegap框架的webapp实例源码
android之PhoneGap入门实例http://blog.csdn.net/beifengdelei/article/details/7702461