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

        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();
			}
		};

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

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

        如果采用其他语言实现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");
})();

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值