之前分析了與平臺無關的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);