第11章 广播中心
前线短信(FrontlineSMS)是一款工具软件,用于联络那些无法访问互联网但可以用手机通信的人,通常用于发展中国家或互联网尚未普及地区的选举监督、天气预报广播等。该软件的作者Ken Banks借助于移动通信技术为人们提供帮助,他的贡献大概无人能及。
该软件可以在一个小组内使用,为组内成员提供短信息接收及广播服务。任何人可以发送一个特殊代码来加入小组,随后他们将收到广播中心发来的信息。对于那些无法访问互联网的地区, 广播中心成为其与外界联系的至关重要的手段。
本章将要创建的“广播中心”应用,与前线短信软件有相似之处,只是“广播中心”运行在安卓手机上。随身携带一台安装了“广播中心”应用的移动设备,意味着管理者可以在移动中与组员之间保持信息交流,这一点在某些场合下尤其重要,如选举监督和医疗谈判等。
假想有一个“快闪舞蹈团”,他们可以召之即来,随时随地表演即兴舞蹈,然后瞬间解散,消失得无影无踪。他们用你创建的“广播中心”来组织表演活动。人们只要向中心发送短信“参加快闪舞蹈团",即可完成入团注册,每个注册成功的人都可以向舞蹈团中的其他人广播消息。
当“广播中心”接收到短信时,有以下几种处理方式。
- 首先判断发信人是否在广播中心的组员名单中,如果不在组员名单中,则回复短信邀请他加入,并告知他申请代码。
- 如果收到短信的内容是“参加快闪舞蹈团”,则将发信人接纳为广播中心成员。{![如果组员发送“参加快闪舞蹈团”呢?应用的逻辑上或许存在漏洞,试看后面如何分解。——译者注]}
- 如果发信人已经是广播中心的成员,则转发该消息给全体广播中心成员。
比起第4章的“开车不发短信”,这个应用更为复杂,不过我们可以采取循序渐进的方式,每一步只完成一项功能。首先,用自动回复来邀请人们加入广播中心。在整个应用完成之后,你将对于创建这类“以短信为用户界面的应用”有透彻的了解。你是否打算写一个短信投票的应用(就像电视选秀节目中使用的那种),或下一款超棒的群组短信应用?本章的学习将使你如愿以偿。
学习要点
本章包括下列App Inventor概念,其中有些你可能已经熟悉了。
- 短信收发器组件:发送短信及处理收到的短信。
- 列表变量及动态数据:在本例中用来记录电话号码清单。
- 遍历列表:对列表中的数据进行逐项重复操作。在本例子中,使用“针对列表()中的每一(项)”块向电话号码列表中的所有手机广播消息。
- 本地数据库组件:实现数据的永久存储,以保证当应用关闭并再次打开时,电话号码列表不丢失。
准备开始
你需要一部可以接收和发送短信的手机来测试程序,还需要招呼一些朋友给你发送短信,来充分地测试应用。
登录App Inventor网站,创建名为“广播中心”的新项目,设置Screen1的标题属性为“广播中心”,并连接测试手机或模拟器。
设计组件
“广播中心”有利于手机之间的通信:这些手机不需要安装应用,甚至不必是智能手机。这些手机通过短信与你的应用通信,因此,应用的用户界面只是为群组的管理人员而设计的。
管理员的用户界面包括两个简单的部分,一是显示当前的“广播列表”,即已注册成员的电话号码清单,二是记录所有收到并已经被广播出去的短信。
为了创建这个界面,要添加表11-1中列出的组件。
表11-1 “广播中心”用户界面中的组件
组件类型 | 所属类别 | 名称 | 作用 |
---|---|---|---|
标签 | 用户界面 | 标签1 | 作为标题放在电话号码列表上方,显示“已注册的电话号码” |
标签 | 用户界面 | 广播列表标签 | 显示已经注册的全部电话号码 |
标签 | 用户界面 | 标签2 | 作为标题放在日志信息上方,显示“活动日志” |
标签 | 用户界面 | 广播日志标签 | 显示短信收发日志 |
短信收发器 | 社交组件 | 短信收发器1 | 处理短信息 |
本地数据库 | 数据存储 | 本地数据库1 | 永久保存电话号码列表 |
添加组件之后,还要设置以下属性:
- 设置每个标签的宽度属性为“充满”,让组件在水平方向上充满手机;
- 设置标题标签的字号属性(标签1和标签2)为18,并勾选粗体属性;
- 设置广播列表标签及广播日志标签的高度为200像素,用于显示多行文本;
- 设置广播列表标签的显示文本属性为“广播列表...”;
- 设置广播日志标签的显示文本属性设置为空;
- 设标签1的显示文本为“已注册的电话号码”;
- 设标签2的显示文本为“活动日志”。
图11-1显示了设计视图中应用的布局。
为组件添加行为
在这个应用中,促使程序运行的事件是其他手机发来的短信,而不是用户在界面上的输入或点击,因此应用的任务是处理这些短信,并将发信人手机号码保存到列表中。具体操作如下。
- 如果短信发送者不在广播列表中,则回复一个邀请加入的短信。
- 如果收到短信“参加快闪舞蹈团”,则将发送者注册为广播列表的一员。
- 如果短信发送者已经在广播列表中,则将该短信广播到列表中的所有手机。
回应收到的短信
现在开始创建第一个行为:收到短信时,回复发送者,邀请他注册。方法是向你发送短信“参加快闪舞蹈团”。表11-2中列出了需要的块。
表11-2 邀请人们通过发短信来加入群组,需要下面的块
代码块 | 所在抽屉 | 作用 |
---|---|---|
当短信收发器1收到消息时 | 短信收发器1 | 当手机收到短信时,触发该事件 |
设短信收发器1的电话号码为 | 短信收发器1 | 设置接收回信的电话号码 |
电话号码 | 从收到消息事件中拖出 | 收到消息事件携带的参数,是短信发送人的电话号码 |
设短信收发器1的短信为 | 短信收发器1 | 设置即将发送的短信内容 |
文本“回复‘参加快闪舞蹈团’加入我们!” | 文本 | 邀请加入的短信内容 |
让短信收发器1发送消息 | 短信收发器1 | 执行发送短信操作 |
块的作用
如果你已经学完第4章的“开车不发短信”应用,应该很熟悉这些块。当手机收到短信时会触发短信收发器1的收到消息事件。如图11-2所示,在事件处理程序中设置短信收发器1的电话号码及短信属性,然后发送短信。
测试:需要用第二部手机来测试这一功能;你不能给自己发短信,否则会永远循环下去!如果没有其他手机,可以注册Google Voice或类似的服务,从这些服务中给自己的手机发短信。用第二部手机发送“你好”到测试手机,则第二部手机会收到一个邀请加入群组的短信。
接纳申请人加入广播列表
现在创建第二个行为所需要的块:收到“参加快闪舞蹈团”的短信后,将发信人的电话号码添加到广播列表中。首先,定义列表变量“广播列表”来保存已注册的电话号码。从变量抽屉中拖出一个声明变量块,将“我的变量”改为“广播列表”,并从列表抽屉中拖出“空列表”块,为变量设置初始值。如图11-3所示(稍后将实现向列表中添加列表项的功能)。
下面修改短信收发器1收到消息事件的处理程序,如果收到短信“参加快闪舞蹈团”,则将发信人手机号码添加到广播列表中。判断短信内容需要使用“如果...则...否则”块(在第10章“出题”应用中使用过),将新号码添加到列表中需要使用“向列表()添加项()”块。整个设置所需的块见表11-3。在电话号码添加完毕之后,用广播列表标签来显示新列表。
表11-3 检查来信内容,并将发信人添加到广播列表中,需要如下代码块
代码块 | 所在抽屉 | 作用 |
---|---|---|
如果...则...否则 | 控制 | 对收到短信的内容进行判断,并依据判断结果执行相应操作 |
等于 | 数学 | 判断来信内容是否等于“参加快闪舞蹈团” |
消息内容 | 从收到消息事件中拖出 | 填充到等号的左边 |
文本“参加快闪舞蹈团” | 文本 | 填充到等号的右边 |
向列表()添加项() | 列表 | 将申请人的电话号码添加到广播列表中 |
global 广播列表 | 从声明变量块中拖出 | 填充到添加列表项块的“列表”插槽中 |
电话号码 | 从收到消息事件中拖出 | 填充到添加列表项块的“项”插槽中 |
设广播列表标签的显示文本为 | 广播列表标签 | 显示新列表 |
global 广播列表 | 从声明变量块中拖出 | 作为广播列表标签的显示文本属性 |
设短信收发器1的短信为 | 短信收发器1 | 设置即将发送短信的内容 |
文本“恭喜你...” | 文本 | 祝贺申请人加入群组 |
块的作用
如图11-4所示,要对刚收到的短信进行回复,因此,第一行代码将发信人手机号码设置为接收人手机号码,即设置短信收发器1的电话号码属性为收到消息事件中的电话号码参数。然后,判断事件中携带的消息内容参数是否为特殊代码“参加快闪舞蹈团”。如果是,则将发送者手机号添加到广播列表,并回复短信祝贺;如果不是,则回复邀请短信。在“如果...则...否则”块之后,回复短信被发出(最后一行)。
测试:用第二部手机发送短信“参加快闪舞蹈团”到测试手机,在测试手机收到短信的同时,第二部手机的号码出现在“已注册的电话号码”下面,第二部手机会收到祝贺短信。尝试发一个其他内容的短信,检查邀请短信是否能正常发送。
广播消息
下面来添加广播消息功能:当广播列表中的成员向广播中心发来短信时,将此信息转发给列表中的所有手机。这一功能略微有些复杂,需要更多的控制块:增加一个“如果...则...否则”块和一个遍历列表块。新增的“如果...则...否则”块用于检查发送短信的手机号是否在广播列表中,而遍历列表块用于向列表中的所有手机广播这条短信。另外还要将之前的“如果...则...否则”块移动到新的“如果...则...否则”块的“否则”分支中。表11-4列出了需要新增的块。
表11-4 向广播列表中的成员群发短信需要新增的块
代码块 | 所在抽屉 | 作用 |
---|---|---|
如果...则...否则 | 控制 | 判断发信人是否在广播列表中,并根据判断结果执行不同操作 |
列表()中包含项() | 列表 | 检查某个值是否为列表项 |
global 广播列表 | 从声明变量块中拖出 | 填充到“列表中包含项”块的“列表”插槽中 |
电话号码 | 从收到消息事件块中拖出 | 填充到“列表中包含项”块的“项”插槽中 |
针对列表()中的每一(项) | 控制 | 向列表中的每个电话号码发送短信 |
global 广播列表 | 从声明变量块中拖出 | 填充到遍历列表块的“列表”插槽中 |
设短信收发器1的短信为 | 短信收发器1 | 设置短信内容 |
消息内容 | 从收到消息事件块中拖出 | 收到短信的内容,稍后将广播出去 |
设短信收发器1的电话号码为 | 短信收发器1 | 设置接收短信的电话号码 |
电话 | 从遍历列表块中拖出 | 遍历广播列表时,当前正在处理的项,为电话号码 |
块的作用
这里使用了嵌套的“如果...则...否则”块,使得程序变得更加复杂,如图11-5所示。嵌套的“如果...则...否则”块指的是在一个“如果...则...否则”块的“则”或“否则”分支中又嵌入了另一个“如果...则...否则”块。在本例中,外层的“如果...则...否则”块负责检查发信人的手机号是否已在广播列表中。如果在,则将该短信转发给列表中的所有人;如果不在,则执行内层“如果...则...否则”判断:消息内容是否为“参加快闪舞蹈团”,并依据判断结果,执行不同的分支操作。
从理论上,“如果...则”块和“如果...则...否则”块可以做任意层级的嵌套,来实现更加复杂的逻辑(更多关于条件语句块的内容,请参见第18章)。
在外层“如果...则...否则”块的“则”分支中,使用遍历列表块来广播短信。通过遍历广播列表中的每一个电话号码,来设置接收短信的电话号码,并将短信发送给该号码。在遍历广播列表的过程中,列表中的每个电话号码依次被保存在占位符变量“电话”中(“电话”是一个变量,代表了遍历过程中当前正在处理的项)。在遍历列表块内,设置短信收发器1的电话号码为当前项“电话”,并向其发送短信。有关遍历列表的更多信息,请参见第20章。
测试:除了测试手机之外,还需要另外两部手机向测试手机发送“参加快闪舞蹈团”,以便注册成为广播列表成员。然后,从一部手机向广播中心发送一条短信,这时两部手机都应该收到这条短信(包括发送短信的那一个)。
美化列表的显示
广播短信的功能已经实现,但管理员的用户界面尚需改进。首先,电话号码列表显得很混乱。用标签显示列表时,列表项之间用空格分隔,并且尽可能占满一行,像下面这样:
(+861303318989 +861581235590 +8618902018909 +8613301103355 +8613801237890)
为了改善这种局面,使用表11-5列出的块创建一个名为“显示广播列表”的过程,来实现每行只显示一个号码。请务必在添加列表项块的下面调用该过程,以便显示更新后的列表。
表11-5 改进电话号码列表显示所需的块
代码块 | 所在抽屉 | 作用 |
---|---|---|
定义过程()执行 | 过程 | 创建一个过程(注意不要选择“定义过程()返回”) |
设广播列表标签的显示文本为 | 广播列表标签 | 显示广播列表 |
文本“” | 文本 | 清空广播列表标签中的原有内容 |
针对列表()中的每一(项) | 控制 | 遍历所有电话号码 |
global 广播列表 | 从声明变量块中拖出 | 填充到遍历列表块的“列表”插槽中 |
设广播列表标签的显示文本为 | 广播列表标签 | 每遍历到一个列表项时,修改标签的显示内容 |
拼字串 | 文本 | 将多个文本片断合并成一段文本 |
广播列表标签的显示文本 | 广播列表标签 | 每遍历到一个列表项时,在现有显示文本基础上添加新内容 |
文本“\n” | 文本 | 添加换行符,以便下一个电话号码在下一行显示 |
电话 | 从遍历列表块中拖出 | 列表中正在被访问的列表项 |
块的作用
“显示广播列表”过程里的遍历列表块逐项地将每个手机号码添加到标签的末尾,如图11-6所示,用换行符(\n)来分隔每个号码,使得每个号码各占一行。
不过,“显示广播列表”过程不会主动做任何事情,除非调用它。在短信收发器1的收到消息事件中,在添加列表项块之后调用它。过程的调用取代了对广播列表的直接显示。在过程抽屉中,可以找到“调用显示广播列表”块。
图11-7显示了对收到消息事件的处理。
关于用遍历列表项的方法来显示列表内容的详细信息,请参见第20章;关于创建和调用过程的详细信息,请参见第21章。
测试:重新启动应用来清除列表,然后用至少两部不同的手机重新进行注册。手机号码是否分行显示了?
记录广播过的短信
在收到短信并向其他手机发出广播之后,程序应该记录此类事件,以便管理员可以对活动进行监督。已经在设计视图中添加的“活动日志标签”就是用于这一目的。下面编写程序,每当收到并广播新的短信时,改变活动日志标签的显示内容。
要创建像这样的一段文本:“来自+8613901231234的短信已经广播。”字符“+8613901231234”不是固定数据,而是收到消息事件中携带的参数。因此,要创建的文本包括三个部分:①“来自”;②手机号码,为事件携带的手机号码参数;③“的短信已经广播”。
正如在前几章中所做的一样,用拼字串块将三个部分连接起来,表11-6中列出了所需要的块。
表11-6 构建活动日志所需要的块
代码块 | 所在抽屉 | 作用 |
---|---|---|
设活动日志标签的显示文本为 | 活动日志标签 | 显示日志内容 |
拼字串 | 文本 | 组合多个文本片断 |
文本“来自” | 文本 | 第一部分待合并的文本片断 |
电话号码 | 从收到消息事件块中拖出 | 第二部分待合并的内容(动态) |
文本“的短信已经广播 \n” | 文本 | 第三部分待合并的文本片断,包含换行符\n |
活动日志标签的显示文本 | 活动日志标签 | 在原有内容基础上添加新内容 |
块的作用
在收到短信后,向广播列表中的所有号码广播此短信,再修改活动日志标签,记录刚才的广播操作,如图11-8所示。需要注意的是,我们将消息添加到列表的开始,而不是结尾,因此最后发出的消息将显示在最顶端。
拼字串块创建了一条新记录:来自+8613901231234的短信已经广播。
每次短信广播之后,这样的一条记录就被添加到活动日志标签的第一行,使最新的记录一直出现在顶部。拼字串块中各个文本片段的顺序决定了日志中记录的顺序。在本例子中,新消息被编排在前三个插槽中,而活动日志标签的显示文本,即已经保存的现有记录,则放在最后一个插槽中。
字符串“的短信已经广播。\n”中的“\n”称为换行符,它让每条记录单独占一行,像这样:
来自+8613030123668的短信已经广播。 来自+8613901231234的短信已经广播。
关于使用遍历列表块来显示列表内容的详细信息,请参见第20章。
将广播列表保存到数据库中
现在应用算是大功告成了,但通过前几章的学习,你可能已经猜到,这里存在一个问题:如果管理人员将应用关闭再重新打开时,广播列表中的数据将全部消失,每个人都必须重新注册。为了解决这个问题,需要使用本地数据库组件,来保存并提取广播列表中的数据。
这里将使用与“出题”应用(第10章)中相似的方案:
- 每次添加新项时,将列表保存到数据库中;
- 应用启动时,从数据库中加载列表,并保存到一个变量中。
用表11-7中所列出的块,将列表存储到数据库中。本地数据库组件中的参数数据标记作为数据的标识,将保存在数据库中的不同数据区分开来。在本例中,你可以将数据标记设为“广播列表”。在短信收发器1的收到消息事件中,将这些块放在添加列表项块之下。
表11-7 用本地数据库来存储广播列表所需要的块
代码块 | 所在抽屉 | 作用 |
---|---|---|
让本地数据库1保存数据 | 本地数据库1 | 将数据保存到本地数据库中 |
文本“广播列表” | 文本 | 填充到保存数据块的“标记”插槽中 |
global 广播列表 | 从声明变量块中拖出 | 填充到保存数据块的“数值”插槽中 |
块的作用
当应用收到短信“参加快闪舞蹈团”,并将新成员的手机号码添加到列表时,调用本地数据库1的保存数据功能,将广播列表保存到数据库中。标记(“广播列表”)的使用是数据存储的需要,也是之后从数据库提取数据的需要。如图11-9所示,保存数据块的另一个参数“数值”则是广播列表变量。
从数据库中加载广播列表
每次应用启动时,从数据库中加载广播列表,表11-8中列出了实现这一功能需要的块。
表11-8 应用启动时加载广播列表所需要的块
代码块 | 所在抽屉 | 作用 |
---|---|---|
当Screen1初始化时 | Screen1 | 应用启动时触发该事件 |
让本地数据库1请求数据 | 本地数据库1 | 从本地数据库中请求数据 |
文本“广播列表” | 文本 | 填充到请求数据块的标记插槽中 |
调用显示广播列表 | 过程 | 数据加载成功后显示数据 |
应用的启动将触发Screen1的初始化事件,因此数据的加载要在该事件的处理程序中实现。
块的作用
应用启动时,将触发Screen1的初始化事件。如图11-10所示,使用“让本地数据库1请求数据”块向数据库请求数据。
在使用本地数据库组件向数据库请求数据时,使用的标记参数与保存数据时的标记(广播列表)相同。一般情况下,将返回此前保存的电话号码列表,并将列表保存到全局变量广播列表中。不过,请求数据块还提供了另外一个参数插槽:“无标记返回”,用来定义当请求的标记不存在时的返回值。例如,当第一次启动应用时,在这种情况下,则返回一个空列表。
测试:你可以使用实时测试功能,来测试那些涉及数据库更新的应用{![较早前的App Inventor版本,凡是涉及本地数据库更新的应用,都无法进行实时测试。因为每次连接测试设备时,都会清空数据库。——译者注]},但要小心操作。在这个例子中,使用其他手机向测试手机发送短信,将手机号码添加到广播列表中,然后重新启动应用。这里告诉你一个重新启动应用的方法:在设计视图中,随便修改某个组件的属性,如改变标签的字体等属性。不过要全面测试数据库应用,还需要将应用打包,下载并安装到手机上(点击“编译→显示二维码或下载到本地“)。在手机上启动应用,用其他手机发送短信加入群组,再退出应用。当重启应用时,如果那些电话号码还在,说明数据库部分工作正常。
完整的“广播中心”应用
图11-11中列出了“广播中心”应用中的全部代码块。
改进
在庆祝完成一个如此复杂的应用时,你也许还想做进一步的完善。举例如下。
- 在广播短信环节,广播中心向所有人发出短信,也包括发送这条短信的列表成员。修改此功能,将短信群发给除了发送者之外的所有成员。
- 允许列表成员退出群组,用手机发送短信“退出”给广播中心,请求从列表中删除自己。这项功能需要使用列表的删除指定项功能。
- 管理人员可以在操作界面上添加或删除广播列表中的成员(手机号)。
- 管理人员可以指定某些不允许加入列表的手机号。
- 细化应用的功能,让任何人都可以加入列表并接收广播,但只有管理员可以广播消息。
- 进一步细化应用,让任何人都可以加入列表并接收广播,但只有一个固定列表中的电话号码可以向全体成员广播消息。
小结
以下是本章涵盖的内容。
- 应用不仅可以响应用户发起的事件,也可以响应非用户发起的事件,比如收到短信这样的事件。这意味着你可以开发这样的应用,应用的用户是其他手机的使用者。
- 使用嵌套的“如果...则...否则”块以及遍历列表块,可以构造出复杂的逻辑。有关条件语句和循环语句的详细信息,请分别参见第18章及第20章。
- 使用拼字串块可以创建一个由多项内容组成的文本对象。
- 本地数据库组件可以用于数据库操作:存储及提取数据。最常用的方法是,在数据发生变化时,调用保存数据功能更新数据库;在应用启动时,调用请求数据功能从数据库中读取数据。