android phonegap源码详解(三)
传送门:
PhoneGap源码详解一
PhoneGap源码详解二
PhoneGap源码详解三
一、 PhoneGap 的启动
近期因为赶项目和犯懒,所以一直没有更新,希望朋友们见谅。
前面介绍过 phoneGap 的模块化机制。其通过 require 和 define ,巧妙的将模块的定义和依赖关系隔离开来。
如上图所示,在定义模块机制后。后面统统都是做 define 。那么,这些被定义的模块最终何时被实例化呢?
在 cordova.js 的末尾我们找到了这一句。第一个被请求实例化的模块是 cordova(adobe 版本是 phonegap) ,接着在实例化 cordova 模块后, cordova 依赖的模块也纷纷被实例化出来了。 (channel 、 utils)
cordova 模块仅是个最基础的顶层通信模块。除了 cordova 模块外,其余数十个 phonegap 模块都还在沉睡之中。事实上,这一步走完其实 phonegap 已经完成了。 Phonegap 用户只需要 require(‘cordova/plugin/contacts’) 这样,即可访问到 phonegap 的功能。
但是,这样做还不够好。第一 ,require 对于 phonegap 使用者来说并不友好。第二,虽然可以通过 require 得到构建模块的实例,可这并不代表 Native/Javascript 的通信关系已经建立。 phonegap 用户还不知道何时可以安全正确的使用这些功能。
基于上面两点, phonegap 构建了一个 bootstrap 函数。在上图的后一句,大家可以看到 bootstrap 的封闭调用。下面我来详解一下 bootstrap
二、 Bootstrap 详解
首先贴上代码。
(function (context) { var channel = require("cordova/channel"), _self = { boot: function () { /** * Create all cordova objects once page has fully loaded and native side is ready. */ channel.join(function() { var builder = require('cordova/builder'), base = require('cordova/common'), platform = require('cordova/platform'); // Drop the common globals into the window object, but be nice and don't overwrite anything. builder.build(base.objects).intoButDontClobber(window); // Drop the platform-specific globals into the window object // and clobber any existing object. builder.build(platform.objects).intoAndClobber(window); // Merge the platform-specific overrides/enhancements into // the window object. if (typeof platform.merges !== 'undefined') { builder.build(platform.merges).intoAndMerge(window); } // Call the platform-specific initialization platform.initialize(); // Fire event to notify that all objects are created channel.onCordovaReady.fire(); // Fire onDeviceReady event once all constructors have run and // cordova info has been received from native side. channel.join(function() { channel.onDeviceReady.fire(); }, channel.deviceReadyChannelsArray); }, [ channel.onDOMContentLoaded, channel.onNativeReady ]); } }; // boot up once native side is ready channel.onNativeReady.subscribeOnce(_self.boot); // _nativeReady is global variable that the native side can set // to signify that the native code is ready. It is a global since // it may be called before any cordova JS is ready. if (window._nativeReady) { channel.onNativeReady.fire(); } }(window));
我们先来详细关注下, phonegap 是如何提供回调通知,来告诉用户 Native 与 Javascript 之间成功建立联系的呢?
首先, bootstrap 通过 channel 模块注册监听了 onNativeReady 事件。这个事件由 Native 层触发,用来表示 Native 层准备完毕 ( 可以接受 plugin 调用 ) 。在 Android 平台上面 ,onNativeReady 是在 WebView 的 onPageFinished 回调中触发的。
为了安全的启动 ,boot 等待 onNativeReady 和 onDOMContentLoaded 完毕后才执行。那么 Phonegap 的 boot 都做了些什么呢 ?
boot 做了两件事情。首先是实例化和发布模块来给 phonegap 用户使用。其次是广播 onCordovaReady 来通知 phonegap 层 boot 完毕 , 用户可以放心使用 phonegap 的功能。
通过 builder, 模块的实例化和发布工作变得很艺术。 builder 模块提供三种发布方式 , 分别是 intoButDontClobber( 若发布目标中已存在同名模块 , 则不允许做覆盖 ),intoAndClobber( 若发布目标中已存在同名模块 , 则强制覆盖 ),intoAndMerge( 若发布目标中已存在实例模块 , 则尝试对两者进行合并,合并的优先级是欲发布模块比发布模块高 )
解释一下“发布”这个词。其实就是根据定义 id 实例化 (require) 模块 , 然后把它作为某个 object 的属性。从 boot 的源码中,我们可以看到。在 common.js 和 platform.js 中,分别定义了 objects 对象。 objects 定义了各个模块的 id 和层次关系。父子关系通过 children 字段表明。
objects: { cordova: { path: 'cordova', children: { exec: { path: 'cordova/exec' } } },
根据上面的定义, cordova 模块下将挂一个 exec 模块。即通过 builder 后 , 用户可以直接通过 cordova.exec 来访问 exec 模块。
当然 , 发布的 target 都是 window 。 common 下的发布定义 (objects) ,所使用的发布策略是不覆写原有属性。之所以这样做,是考虑到之后浏览器加强 html5 支持后,有些模块将会原生提供。
platform 下的发布定义 (objects) 所使用的发布策略是覆写所有属性。其定义的是与平台密切相关的模块,因此会做强制覆写。
注意 platform 下的发布定义,除了 objects 外。还提供了 merge 定义。这里的 merge 作用在于“增强”。为原有的通用模块,增加一些平台相关的其它方法。在 android/platform.js 的 merges 定义便是一个例子。最终发布的 notification 将既包含 common 下 notification.js 的 alert 方法,也包含 platform 下 notification.js 的 activityStart 方法。
发布模块后会执行 platform.initialize 方法。这个方法用于做 platform 初始化工作,将与平台特性紧耦合,尤其会与 Native 与 Javascript 互相通信相关。在 android 平台版本中 ,initialize 包含了 polling/xhr 的初始化, android 的按键事件、可兼容 web 数据库 api 与 localstorage 。当这些工作完毕后,将会广播 onCordovaReady 事件。此后便可以安全的使用 phonegap 所提供的所有功能了。
onCordovaReady 后会去准备 onDeviceReady 。关于 onDeviceReady 的详细描述,我在上篇解析的 channel 中提过了,在此也就不重复了。
三、 总结
至此 phonegap 的 Native 与 Javascript 层源码解析完毕。有关如何扩展 phonegap 插件,各位可以参照 github 上面的这个开源项目: https://github.com/purplecabbage/phonegap-plugins
需要注意的是。其中的 addPlugin 写法 ,phonegap 已经不再推荐使用了。并表示该方法将在 2.0 版本中移除。
由于本人真正接触 phonegap 时间并不长,难免会有解释不当的地方。欢迎指正!也欢迎跟帖讨论!