AppWorker教程-事件机制

事件和消息机制的介绍

消息/事件机制是几乎所有开发语言都有的机制,并不是AppWorker的独创,在某些语言称之为消息(Event),有些地方称之为(Message).。其实原理是类似的,只不过有些实现的方式要复杂一点。我们AppWorker统一就叫消息。

消息基础概念

还有一些初学者不太熟悉这个机制,我们先简单介绍一些基础概念,如果熟悉的人可以跳过这个部分。
一个/条消息可以理解为是一个数据结构,包含以下几个基本部分:

消息源  :就是消息的来源,发出这个消息的对象
消息名  :就是消息的唯一标示
消息数据:消息发出后附带的数据,有可能数据是空

消息从种类上又可以分为2种:

系统消息  :由操作系统或AppWorker系统发送出来的消息,消息的名称是固定的。
自定义消息:由开发者自己定义,自己发送出来的消息,消息的名字是随意的,可以任意定义。

举例说明:
比如用户点击一个do_Button按钮,就会触发一个系统消息,包含3个部分:

消息源:用户点中的button对象
消息名:touch
消息数据:这个消息没有附带数据

比如用户通过do_Button按钮触发一个自定义事件,包含3个部分:

消息源: button对象
消息名:用户随便定义,叫aaa,bbb,ccc都可以
消息数据:附带的数据由触发消息的时候设定

发布/订阅模式

发布/订阅模式是最常用的设计模式之一,是消息机制的核心,其特点就是降低耦合度,让二个独立的对象不互相依赖。简单介绍一下,熟悉的同学可以跳过。
我们先从现实的一个简单例子来说明这个问题,参考下图:


image

从这个图我们可以看出

消费者和出版社互相不认识,消费者不需要了解他想要的杂志是具体哪家出版社出的;出版社也不需要了解具体是哪个人定了他们出版社发行的书。
消费者和出版社必须都认识邮局。
消费者需要告诉邮局消费者的名字地址以及想要订阅的杂志名字
可以多个消费者订阅同一本杂志
邮局拿到杂志后,会一一通知消费者,通知的时候同时把杂志送到消费者手里。

看完上面现实例子,我们再来看抽象的描述会更清晰一点,看下图:


image

和上面的实际例子描述一一对应:

系统/开发者和函数对象互相不依赖,系统/开发者只管触发一个消息,并不关心谁去接受
系统/开发者和函数对象必须能获取到消息源对象
函数对象订阅消息的时候需要标示消息的名称和函数对象的引用
可以多个函数对象订阅同一个消息源同一名字的消息
消息源触发消息会一一通知所有订阅者,并把data数据传递到回调函数对象

看完抽象的描述,我们最后来看实际的AppWorker开发的例子,还是以do_Button为例子。

1. 当用户点击一个button,触摸到的时候,系统会获取到button这个对象作为消息源,fire一个"touch"消息,任何订阅了"touch"消息的函数对象都会接收到这个消息并引起函数的执行。
//获取button对象
var btn_hello = ui("btn_hello");
//定义函数对象
function f1(){
    //当btn_hello这个按钮接收到手指点击就会执行下面的代码
    print("f1 函数接收到点击触发消息")
}
function f2(){
    //当btn_hello这个按钮接收到手指点击就会执行下面的代码
    print("f2 函数接收到点击触发消息")
}
//f1,f2订阅button的touch消息
btn_hello.on("touch",f1);
btn_hello.on("touch",f2);
2. 我们可以为button对象定义2个自定义的消息"message1"和"message2",分别有2个函数对象订阅这2个消息。但是最后要触发这个消息必须是开发者通过调用fire函数才能触发,这就是和系统消息的区别。
//获取button对象
var btn_hello = ui("btn_hello");
//定义函数对象
function f1(d){
    //当btn_hello这个按钮接收到开发者触发的消息message1就会执行下面的代码
    print("f1 函数接收到message1消息,消息的数据是:"+d)
}
function f2(d){
    //当btn_hello这个按钮接收到开发者触发的消息message2就会执行下面的代码
    print("f2 函数接收到message2消息,消息的数据是:"+d)
}
//f1,f2订阅button的touch消息
btn_hello.on("message1",f1);
btn_hello.on("message2",f2);

//触发消息
btn_hello.fire("message1","data1");
btn_hello.fire("message2","data2");

看到这里,你肯定会奇怪,为什么我们要在button上自定义对象?这有神马意义?其实确实没有意义也没有必要,这里只是拿button举例子,在常规的开发中,基本不会这么用。

消息的使用

前面讲了这么多,现在才是AppWorker消息的使用。使用其实很简单,上面的例子基本说明的了系统事件和自定义事件的使用方法。
有几个概念再说明一下

1.AppWorker的所有对象,包括UI,MM,SM对象都可以是消息源
// SM对象可以是消息源
var page = sm("do_Page");
page.on("loaded",function()){
    // 这个是page对象的系统消息,这个消息不需要手动触发,系统会自动触发
}
page.on("message1",function(d)){
    // 这个是page对象的自定义消息
}
page.fire("message1","data1");

// MM对象可以是消息源
var http = mm("do_Http");
http.on("result",function()){
    // 这个是http对象的系统消息,这个消息不需要手动触发,接受到http服务端的反馈后会自动触发
}
http.on("message1",function(d)){
    // 这个是http对象的自定义消息
}
http.fire("message1","data1");

//UI对象可以是消息源
var alayout = ui("alayout_id1");
alayout.on("touch",function()){
    // 这个是alayout对象的系统消息,这个消息不需要手动触发,手机点击就会触发
}
alayout.on("message1",function(d)){
    // 这个是alayout对象的自定义消息
}
alayout.fire("message1","data1");
2.消息源对象有作用域,所以订阅和触发的消息源必须是是一个作用域的同一个对象。这里结合数据分享和数据传递文档来理解。

看以下的例子,test1.ui和test2.ui有可能在一个page作用域,也有可能不在一个作业域,只有在一个作用域fire的消息才能正确送达回调函数。
判断是否一样,可以通过打印page的地址 page.getAddress().

//在test1.ui.js里订阅消息
var page = sm("do_Page");
print(page.getAddress());
page.on("message1",function(d)){
    print(d);
}
//在test2.ui.js触发消息
var page = sm("do_Page");
print(page.getAddress());
page.fire("message1","data1");

如果不在同一page作用域,则可以把消息订阅在2个page都能共享到的app作用域
上面的代码改成:

//在test1.ui.js里订阅消息
var app = sm("do_App");
app.on("message1",function(d){
    print(d);
});
//在test2.ui.js触发消息
var app = sm("do_App");
app.fire("message1","data1");
3.同样的函数对象可以重复订阅一个对象源的消息,触发消息的时候会使函数执行多次,这是初学者经常犯的错误。
var page = sm("do_Page");
var count = 0;
function f(){
    print("执行次数"+(count++));
}
page.on("message1",f);
page.on("message1",f);
page.fire("message1");

看上面的例子,如果执行的话,会打印2此,因为订阅了2次,或许你会说谁会写这样的代码?实际情况肯定没有这么容易看出来执行了重复的on函数,实际情况经常是比如在点击事件里执行on函数,每点击一下按钮,就重复订阅一次。

4.消息的订阅一定要在消息的触发之前,这是初学者经常犯的错误。
var page = sm("do_Page");
var count = 0;
function f(){
    print("执行次数"+(count++));
}
page.fire("message1");
page.on("message1",f);

看上面的例子,如果执行的话,会没有效果,或许你会说谁会写这样的代码?实际情况肯定没有这么容易看出来顺序反了,实际情况经常是比如on函数执行在某一个函数的回调函数里,你无法确定回调函数啥时候执行,是否是在fire之前执行。print打印一下看看是on先执行还是fire先执行。

5.有订阅就有取消订阅,取消订阅是off函数,之所以很少用,是因为closePage的时候会自动把当前page作用域订阅的消息全部释放。但是如果消息订阅在app作用域,就要注意,可能需要手动去取消订阅。否则就会出现触发消息的时候会使函数执行多次的问题。
var page = sm("do_Page");
var count = 0;
function f(){
    print("执行次数"+(count++));
}
page.on("message1",f);
page.on("message1",f);
page.fire("message1");

page.off("message1");
page.fire("message1");

看上面的例子,off前会打印执行二次,off后就不会打印了,因为fire一次后就取消订阅了,而且是取消所有订阅"message1"的消息。

6.消息订阅的delay参数

有的时候我订阅了屏幕上一个按钮,去openPage一个新的页面,然后我用手指快速点击的时候会触发多次事件,导致同一页面打开多次;还有就是Android的返回物理键实现closePage,如果连续按就关闭很多层page,很显然这些都不是是我们期望的。delay参数就是解决这个问题的:

var btn_hello = ui("btn_hello");
//3000毫秒之类只能触发一次touch事件
btn_hello.on("touch", "", 3000, function() {
    print("我被点中了");
});

我测试的时候手指一直不停的点击,但是在3秒内只能触发一次,参考打印的截图:


image
7.消息订阅的data参数

这个参数用的比较少,在一些特殊的情况下使用会比较方便,在订阅的时候传递一个data值作为标示,在回调的时候可以获取到这个标示为透传数据来区分订阅者。见示例:

for(i=0;i<10;i++){
    var button = ui("buttonid_"+i);
    button.on("touch","buttonid_"+i,touchFun)
}
function touchFun(d,e){
    if(e.data==="buttonid_0"){
        //点击按钮0做什么操作....
    }else if(e.data==="buttonid_1"){
        //点击按钮1做什么操作....
    }
    ...
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容

  •   JavaScript 与 HTML 之间的交互是通过事件实现的。   事件,就是文档或浏览器窗口中发生的一些特...
    霜天晓阅读 3,483评论 1 11
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,644评论 18 139
  •   JavaScript 是一种极其灵活的语言,具有多种使用风格。   一般来说,编写 JavaScript 要么...
    霜天晓阅读 742评论 0 0
  • 未漾哒阅读 299评论 0 0
  • 1 for语句 for(;;){} 注意变量作用域 2 while && do...while语句 do...wh...
    _简书阅读 105评论 0 0