之前分析了与平台无关的common模块,这节来分析下与平台相关的platform模块和phonegap的初始化。
define("myphonegap/platform", function(require, exports, module) {
module.exports = {
id: "android",
initialize:function() {
}
clobbers: { //需要覆盖的属性
navigator: {
children: {
app:{
path: "myphonegap/plugin/android/app"
}
}
},
//省略
open: {
path: "myphonegap/plugin/InAppBrowser"
}
},
merges: { //需要合并的属性
device: {
path: 'myphonegap/plugin/android/device'
},
navigator: {
children: {
notification: {
path: 'myphonegap/plugin/android/notification'
}
}
}
}
}
};
该模块是与平台相关的,现在分析的是android平台,所以这部分代码与android平台相关。platform对象实例提供了一个initialize方法用来执行平台初始化代码,id表示不同平台,clobbers和merges与common下的属性作用相同,都是一个字面量对象,作为builder模块函数的参数用来生成相应模块对象;二者区别在于clobbers是属性覆盖,merges是属性合并。
platform和common类似,因为与平台相关,这里暂时不测试了。
当所有define语句执行完成也就是模块定义之后,开始模块初始化或实例化。先请求主模块对象,将主模块对象添加到全局执行环境;之后是一个自执行的匿名函数,context是形参,window是实参。
window.myphonegap = require(‘myphonegap’);
(function (context) {
if (context.navigator) {
}
var channel = require("myphonegap/channel"),
_self = {
boot: function () {
}, [ channel.onNativeReady,channel.onDOMContentLoaded ]);
}
};
channel.onNativeReady.subscribe(_self.boot);
if (window._nativeReady) {
channel.onNativeReady.fire();
}
}(window));
第一个if语句中的代码是对navigator这个变量做处理,让window下的navigator中的所有属性变成了它的原型中的属性。到底有什么意义,不太明白,看代码注释,clobber这个单词没看懂什么意思。
// Replace navigator before any modules are required(), to ensure it happens as soon as possible.
// We replace it so that properties that can't be clobbered can instead be overridden.
if (context.navigator) {
var CordovaNavigator = function() {};
CordovaNavigator.prototype = context.navigator;
context.navigator = new CordovaNavigator();
}
看下面的两个对象的定义,一个是channel通道模块,看来下面的代码涉及到事件订阅了。_self对象中只有一个函数对象boot, boot函数是初始化引导函数,里面执行的是channel.join函数,参数为一个匿名函数和两个通道对象组成的数组。Join函数代码比较费劲,它的第二个参数是个通道对象数组,当所有通道事件触发一次之后,执行第一个参数传递过来的函数。
var channel = require("myphonegap/channel"),
_self = {
boot: function () {
channel.join(function() {
}, [ channel.onNativeReady,channel.onDOMContentLoaded ]);
}
};
然后再看看join函数的第一个参数,匿名函数中的内容。先请求builder、common、platform三个模块对象,然后根据这三个对象构建了其它的模块对象。之后执行平台初始化,触发onMyphonegapReady事件;最后一个语句,将onDeviceReady事件的触发,绑定在 channel.deviceReadyChannelsArray中的所有事件触发之后。
function() {
var builder = require('myphonegap/builder'),
base = require('myphonegap/common'),
platform = require('myphonegap/platform');
builder.buildIntoButDoNotClobber(base.defaults, context);
builder.buildIntoAndClobber(base.clobbers, context);
builder.buildIntoAndMerge(base.merges, context);
builder.buildIntoButDoNotClobber(platform.defaults, context);
builder.buildIntoAndClobber(platform.clobbers, context);
builder.buildIntoAndMerge(platform.merges, context);
//调用平台初始化
platform.initialize();
//触发onMyphonegapReady通道事件
channel.onMyphonegapReady.fire();
// deviceReadyChannelsArray上的所有事件被触发之后,触发第一个参数传入的匿名函数
channel.join(function() {
require('myphonegap').fireDocumentEvent('deviceready');
}, channel.deviceReadyChannelsArray);
}
其实立即上面这段代码的关键是立即join这个函数,这个函数是channel提供的。代码如下:
join: function(h, c) {
var len = c.length,
i = len,
f = function() {
if (!(--i)) h();
};
for (var j=0; j<len; j++) {
if (c[j].state === 0) {
throw Error('Can only use join with sticky channels.');
}
c[j].subscribe(f);
}
if (!len) h();
}
理解这个函数的关键是如何看待f这个对象,给f赋值的是一个函数表达式,函数表达式是不同于函数声明的,当然函数声明是无法直接对变量进行赋值的。这个函数表达式中的变量i此时就是这个函数对象的一个属性了,而且它的初始值就是数组c的长度;函数h就是对象f的另一个属性;当f函数被调用的时候i减1。f函数被提交到了channel通道上,也就是说c是个通道数组,当这个通道数组的所有事件都被触发之后,i才变为0,此时h函数才被执行。
对比下JAVA语言实习类似f函数的功能:f和h都为接口,通道事件订阅是以接口为参数。
interface InterfaceF{
public void f();
}
interface InterfaceH{
public void h();
}
ClassF implements InterfaceF{
private int i = 0;
private InterfaceH interfaceH;
ClassF(int i , InterfacH interfacH){
this.i = i;
this.interfacH = interfacH;
}
Public void f(){
if(--i == 0) interfaceH.h();
}
}
InterfaceF instanceF = new ClassF(c.length, instanceH);