您的位置:首页 > 其它

4(phonegap源码分析)通道模块的事件订阅机制(channel)

2013-08-09 21:09 501 查看
channel模块式所有模块中比较核心的一个模块,它定义一个通道,在这个通道上有一个事件,你可以订阅这个事件,当这个事件被激发的时候,所有注册在这个通道上的订阅者都能收到。这里的订阅者其实就是一个函数,当事件激发,函数就会被调用。

下面是channel的工厂函数的主体代码:

  function(require, exports, module) {
  var utils = require('myphonegap/utils');
  
  var Channel = function(type, opts) {
  },
      channel = {
       };
  
  function forceFunction(f) {
  }
  
  Channel.prototype.subscribe = function(f, c, g) {
  };
  Channel.prototype.subscribeOnce = function(f, c) {
   };
  Channel.prototype.unsubscribe = function(g) {
   };
  Channel.prototype.fire = function(e) {
  };
  
  channel.create('onDOMContentLoaded');
  channel.create('onNativeReady');
  channel.create('onMyphonegapReady');
  channel.create('onMyphonegapInfoReady');
  channel.create('onMyphonegapConnectionReady');
  channel.create('onDeviceReady');
  channel.create('onResume');
  channel.create('onPause');
  channel.create('onDestroy');
  
  channel.waitForInitialization('onMyphonegapReady');
  channel.waitForInitialization('onMyphoneConnectionReady');
  module.exports = channel;
}


上面的代码是JS中比较经典的实现的面向对象的方法:将成员变量定义在构造函数中,将成员函数定义在构造函数的原型对象中。这样通过new方法实例出来的对象,成员变量是相互独立的,而成员函数通过原型对象共享。

Channel这个函数对象可以看做是一个通道类的构造函数,最重要的两个成员变量type存储事件类型,handlers存储事件订阅者(函数)。

var Channel = function(type,sticky){
this.type = type;
//map of guid -> function
this.handlers = {};
// 0 = Non-sticky, 1 = Sticky non-fired, 2 = Sticky fired.
this.state = sticky?1:0;
// Used in sticky mode to remember args passed to fire().
this.fireArgs = null;
// Used by onHasSubscribersChange to know if there are any listeners.
this.numHandlers = 0;
// Function that is called when the first listener is subscribed, or when
// the last listener is unsubscribed.
this.onHasSubscribersChange = null;
}


this指针指向的是当前执行环境,所以通过new构造新的对象的时候,this指向的就是这个新生成的对象,而之后函数原型对象中的函数执行的时候,函数中的this指针执行的就是调用它们的对象。但是Channel当做一个函数执行时,this指针指向的就是全局执行环境,也就是window这个对象,调用Channel也不能返回一个对象。

下面再分析下channel这个对象,注意这个channel是小写字母开头。先上代码:

channel={
create:function(type){
channel[type] = new Channel(type,false);
},
createSticky:function(type){
channel[type] = new Channel(type,true);
},
deviceChannelsArray:[],
deviceChannelsMap:{},
waitForInitialization:function(feature){
if(feature){
var c = channel[feature]||this.createSticky(feature);
this.deviceChannelsArray.push(c);
this.deviceChannelsMap[feature]=c;
}
},
initializationComplete:function(feature){
var c = this.deviceChannelsMap[feature];//
if(c){
c.fire();
}
},
join: function (h, c) {//join也就是将一组Channel连接起来,并注入一个在所有Channel上只执行一次的公共处理函数
var i = c.length;
var len = i;
var f = function() {
if (!(--i)) h();
};
for (var j=0; j<len; j++) {
!c[j].fired?c[j].subscribeOnce(f):i--;
}
if (!i) h();
}
};


如果只看名字,一般人都以为channel是Channel的一个实例,我开始也是这样认为的;而事实上它根本不是Channel的实例,如果勉强一点可以把它看做Channel实例的集合,之所以说勉强,是因为它还提供了一些函数。我想之所以它也取名叫做channel而不叫channelMap或channels的原因是它最终是作为通道模块的导出对象的。

这个channel对象的功能:通过create方法构造各个通道实例,然后可以动过这个对象访问到他已生成的对象实例。比如channel.create('onDeviceReady')创建通道实例之后,就可以通过channel.onDeviceReady来访问这个Channel实例了,每个Channel提供了subscribe、unsubscribe、subscribeOnce、fire四个方法。

如果采用其他语言实现channl这个对象功能,就会写一个ChannelManager的单例。它提供一个create方法,通过不同的通道名生成Channel实例,然后将通道名与实例作为一个键值对存储在Map中,通过一个get函数来访问对应的Channel实例。

ChannelManager.getInstance().create(“onDeviceReady”);
Channel  onDeviceReady = ChannelManager.getInstance().get(“onDeviceReady”);


至于channel提供的waitForInitialization、和initializationComplete这两个函数,一个是等待初始化,一个是初始化完成。等待初始化将根据名称实例化通道加入到一个表中,调用初始化完成函数就激发这些通道事件。

join函数很费解,暂不分析...

下面进行测试,先请求该模块实例,然后再该模块实例的onNativeReady通道对象上注册一个函数,函数内容就是打印一条信息。手动触发这个通道事件,看注册的函数是否执行。

//测试channel模块
var channel = require("myphonegap/channel");
channel.onNativeReady.subscribe(function(){
console.info("onNativeReady");
}
);
console.info("before native ready");
channel.onNativeReady.fire();
console.info("after native ready");

测试结果::



myphonegap.js 源码:

;(function(){
  var  require,//myphonegap内部的工具函数,用来导入相关的模块
  define;//在myphonegap注册相关的模块

  //通过一个立即调用的匿名函数,来给require和define赋上实际的函数
  (function(){
var modules={}; // 模块数组,添加模块类似给这个对象添加了属性,模块名为属性名,模块对象为属性值,或者说是键值对

build = function(module){ //根据模块对象构造模块导出对象,模块导出对象存储在modules这个对象数组内
var factory = module.factory;
module.exports = {}; //给当前模块加入了一个exports属性
delete module.factory; //删除了module的属性
factory(require,module.exports,module); //构建导出模块,module.exports是传出参数(实参,引用传递)
return module.exports;
}

require = function(id){ //根据模块名称/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 + " is exist!";
}
modules[id] = { //定义模块对象,左边的值为属性名,右边的值为传入的参数
id:id,
factory:factory
};
}
  })();

  //注册myphonegap模块
  define("myphonegap", function(require, exports, module){
console.info("create myphonegap module");
var myphonegap = {
Hello:function(name){
console.info("hello, "+name +" !");
}
};

module.exports = myphonegap;
  });

  //注册myphonegap/builder模块
define("myphonegap/builder", function(require, exports, module) {

});

  define("myphonegap/channel",function(require,exports,module){
var utils = require("myphonegap/utils"),
nextGuid = 1;
//典型的创建对象方法:通过构造器初始化变量,从而让各个实例相互独立;之后通过修改函数原型共享实例方法。
var Channel = function(type,sticky){
this.type = type;
//map of guid -> function
this.handlers = {};
// 0 = Non-sticky, 1 = Sticky non-fired, 2 = Sticky fired.
this.state = sticky?1:0;
// Used in sticky mode to remember args passed to fire().
this.fireArgs = null;
// Used by onHasSubscribersChange to know if there are any listeners.
this.numHandlers = 0;
// Function that is called when the first listener is subscribed, or when
// the last listener is unsubscribed.
this.onHasSubscribersChange = null;

}, channel={
create:function(type){
channel[type] = new Channel(type,false);
},
createSticky:function(type){
channel[type] = new Channel(type,true);
},
deviceReadyChannelsArray: [],
deviceReadyChannelsMap: {},
waitForInitialization: function(feature) {
if (feature) {
var c = channel[feature] || this.createSticky(feature);
this.deviceReadyChannelsMap[feature] = c;
this.deviceReadyChannelsArray.push(c);
}
},
initializationComplete: function(feature) {
var c = this.deviceReadyChannelsMap[feature];
if (c) {
c.fire();
}
},
join: function (h, c) {//join也就是将一组Channel连接起来,并注入一个在所有Channel上只执行一次的公共处理函数
var i = c.length;
var len = i;
var f = function() {
if (!(--i)) h();
};
for (var j=0; j<len; j++) {
!c[j].fired?c[j].subscribe(f):i--;
}
if (!i) h();
}
};

function forceFunction(f){
if (f === null || f === undefined || typeof f != 'function') throw "Function required as first argument!";
}
//给对象Channel的原型对象添加订阅函数,参数:函数对象、上下文、全局唯一ID
Channel.prototype.subscribe = function(f,c,g){
forceFunction(f);//确保f为函数

if(this.state==2){ //apply方法能劫持另外一个对象的方法,继承另外一个对象的属性;f里面的指针为c(Channel的实例)
f.apply(c||this,this.fireArgs);
}

var func = f,
guid = f.observer_guid;
if(typeof f == "object"){ func = utils.close(c,f);}

if(!guid){
guid = ''+ nextGuid++;
}
f.observer_guid = guid;
func.observer_guid = guid;

//防止重复添加
if(!this.handlers[guid]){
this.handlers[guid] = func;
this.numHandlers++;
if(this.numHandlers===1){
this.onHasSubscribersChange&&this.onHasSubscribersChange();
}
}

};
Channel.prototype.unsubscribe = function(f){
forceFunction(f);
var guid = f.observer_guid;
if(this.handlers[guid]){
delete this.handlers[guid];
this.numHandlers--;
if(numHandlers===0){
this.onHasSubscribersChange&&this.onHasSubscribersChange();
}
}
};
//订阅一次
Channel.prototype.subscribeOnce = function(f,c,g){

};
//调用了在通道订阅的所有函数
Channel.prototype.fire = function(e){
//console.info('fire start:type/'+this.type + ' numHandlers/' +this.numHandlers);
var fail = false,
fireArgs = Array.prototype.slice.call(arguments);
// Apply stickiness.
if (this.state == 1) {
this.state = 2;
this.fireArgs = fireArgs;
}
if (this.numHandlers) {
// Copy the values first so that it is safe to modify it from within
// callbacks.
var toCall = [];
for (var item in this.handlers) {
toCall.push(this.handlers[item]);
}
for (var i = 0; i < toCall.length; ++i) {
toCall[i].apply(this, fireArgs);

// console.info(this.type+' enter func fire ');
}
if (this.state == 2 && this.numHandlers) {
this.numHandlers = 0;
this.handlers = {};
this.onHasSubscribersChange && this.onHasSubscribersChange();
}
}
};

channel.create('onDOMContentLoaded');
channel.create('onNativeReady');
channel.create('onDeviceReady');

channel.waitForInitialization('onMyphonegapReady');
channel.waitForInitialization('onMyphoneConnectionReady');

module.exports = channel;

//console.info('define myphonegap/channel completed');
});

  //注册myphonegap/common模块
  //配置对象,将公共模块组织起来
define("myphonegap/common",function(require,exports,module){
});

 
define("myphonegap/exec", function(require, exports, module) {
});

  //注册myphonegap/platform模块
  define("myphonegap/platform", function(require, exports, module){
  });

  // 这里省略了其它插件的注册

  //注册myphonegap/utils模块
  define("myphonegap/utils", function(require, exports, module){
});

(function (context) {
}(window));
  //所有模块注册完之后,再导入myphonegap至全局环境中
  //window.myphonegap = require('myphonegap');
//window.myphonegap.Hello("wen");

//测试channel模块 var channel = require("myphonegap/channel"); channel.onNativeReady.subscribe(function(){ console.info("onNativeReady"); } ); console.info("before native ready"); channel.onNativeReady.fire(); console.info("after native ready");
})();
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: