开发体验

第11章 广播中心

前线短信(FrontlineSMS)是一款工具软件,用于联络那些无法访问互联网但可以用手机通信的人,通常用于发展中国家或互联网尚未普及地区的选举监督、天气预报广播等。该软件的作者Ken Banks借助于移动通信技术为人们提供帮助,他的贡献大概无人能及。

该软件可以在一个小组内使用,为组内成员提供短信息接收及广播服务。任何人可以发送一个特殊代码来加入小组,随后他们将收到广播中心发来的信息。对于那些无法访问互联网的地区, 广播中心成为其与外界联系的至关重要的手段。

图11-1 设计视图中的“广播中心”

本章将要创建的“广播中心”应用,与前线短信软件有相似之处,只是“广播中心”运行在安卓手机上。随身携带一台安装了“广播中心”应用的移动设备,意味着管理者可以在移动中与组员之间保持信息交流,这一点在某些场合下尤其重要,如选举监督和医疗谈判等。

假想有一个“快闪舞蹈团”,他们可以召之即来,随时随地表演即兴舞蹈,然后瞬间解散,消失得无影无踪。他们用你创建的“广播中心”来组织表演活动。人们只要向中心发送短信“参加快闪舞蹈团",即可完成入团注册,每个注册成功的人都可以向舞蹈团中的其他人广播消息。

当“广播中心”接收到短信时,有以下几种处理方式。

  1. 首先判断发信人是否在广播中心的组员名单中,如果不在组员名单中,则回复短信邀请他加入,并告知他申请代码。
  2. 如果收到短信的内容是“参加快闪舞蹈团”,则将发信人接纳为广播中心成员。{![如果组员发送“参加快闪舞蹈团”呢?应用的逻辑上或许存在漏洞,试看后面如何分解。——译者注]}
  3. 如果发信人已经是广播中心的成员,则转发该消息给全体广播中心成员。

比起第4章的“开车不发短信”,这个应用更为复杂,不过我们可以采取循序渐进的方式,每一步只完成一项功能。首先,用自动回复来邀请人们加入广播中心。在整个应用完成之后,你将对于创建这类“以短信为用户界面的应用”有透彻的了解。你是否打算写一个短信投票的应用(就像电视选秀节目中使用的那种),或下一款超棒的群组短信应用?本章的学习将使你如愿以偿。

学习要点

本章包括下列App Inventor概念,其中有些你可能已经熟悉了。

  • 短信收发器组件:发送短信及处理收到的短信。
  • 列表变量及动态数据:在本例中用来记录电话号码清单。
  • 遍历列表:对列表中的数据进行逐项重复操作。在本例子中,使用“针对列表()中的每一(项)”块向电话号码列表中的所有手机广播消息。
  • 本地数据库组件:实现数据的永久存储,以保证当应用关闭并再次打开时,电话号码列表不丢失。

准备开始

你需要一部可以接收和发送短信的手机来测试程序,还需要招呼一些朋友给你发送短信,来充分地测试应用。

登录App Inventor网站,创建名为“广播中心”的新项目,设置Screen1的标题属性为“广播中心”,并连接测试手机或模拟器。

设计组件

“广播中心”有利于手机之间的通信:这些手机不需要安装应用,甚至不必是智能手机。这些手机通过短信与你的应用通信,因此,应用的用户界面只是为群组的管理人员而设计的。

管理员的用户界面包括两个简单的部分,一是显示当前的“广播列表”,即已注册成员的电话号码清单,二是记录所有收到并已经被广播出去的短信。

为了创建这个界面,要添加表11-1中列出的组件。

表11-1 “广播中心”用户界面中的组件

组件类型 所属类别 名称 作用
标签 用户界面 标签1 作为标题放在电话号码列表上方,显示“已注册的电话号码”
标签 用户界面 广播列表标签 显示已经注册的全部电话号码
标签 用户界面 标签2 作为标题放在日志信息上方,显示“活动日志”
标签 用户界面 广播日志标签 显示短信收发日志
短信收发器 社交组件 短信收发器1 处理短信息
本地数据库 数据存储 本地数据库1 永久保存电话号码列表

添加组件之后,还要设置以下属性:

  1. 设置每个标签的宽度属性为“充满”,让组件在水平方向上充满手机;
  2. 设置标题标签的字号属性(标签1和标签2)为18,并勾选粗体属性;
  3. 设置广播列表标签及广播日志标签的高度为200像素,用于显示多行文本;
  4. 设置广播列表标签的显示文本属性为“广播列表...”;
  5. 设置广播日志标签的显示文本属性设置为空;
  6. 设标签1的显示文本为“已注册的电话号码”;
  7. 设标签2的显示文本为“活动日志”。

图11-1显示了设计视图中应用的布局。

图11-1 设计视图中的“广播中心”

为组件添加行为

在这个应用中,促使程序运行的事件是其他手机发来的短信,而不是用户在界面上的输入或点击,因此应用的任务是处理这些短信,并将发信人手机号码保存到列表中。具体操作如下。

  • 如果短信发送者不在广播列表中,则回复一个邀请加入的短信。
  • 如果收到短信“参加快闪舞蹈团”,则将发送者注册为广播列表的一员。
  • 如果短信发送者已经在广播列表中,则将该短信广播到列表中的所有手机。

回应收到的短信

现在开始创建第一个行为:收到短信时,回复发送者,邀请他注册。方法是向你发送短信“参加快闪舞蹈团”。表11-2中列出了需要的块。

表11-2 邀请人们通过发短信来加入群组,需要下面的块

代码块 所在抽屉 作用
当短信收发器1收到消息时 短信收发器1 当手机收到短信时,触发该事件
设短信收发器1的电话号码为 短信收发器1 设置接收回信的电话号码
电话号码 从收到消息事件中拖出 收到消息事件携带的参数,是短信发送人的电话号码
设短信收发器1的短信为 短信收发器1 设置即将发送的短信内容
文本“回复‘参加快闪舞蹈团’加入我们!” 文本 邀请加入的短信内容
让短信收发器1发送消息 短信收发器1 执行发送短信操作

块的作用

如果你已经学完第4章的“开车不发短信”应用,应该很熟悉这些块。当手机收到短信时会触发短信收发器1的收到消息事件。如图11-2所示,在事件处理程序中设置短信收发器1的电话号码及短信属性,然后发送短信。

图11-2 收到短信后发送邀请短信

测试:需要用第二部手机来测试这一功能;你不能给自己发短信,否则会永远循环下去!如果没有其他手机,可以注册Google Voice或类似的服务,从这些服务中给自己的手机发短信。用第二部手机发送“你好”到测试手机,则第二部手机会收到一个邀请加入群组的短信。

接纳申请人加入广播列表

现在创建第二个行为所需要的块:收到“参加快闪舞蹈团”的短信后,将发信人的电话号码添加到广播列表中。首先,定义列表变量“广播列表”来保存已注册的电话号码。从变量抽屉中拖出一个声明变量块,将“我的变量”改为“广播列表”,并从列表抽屉中拖出“空列表”块,为变量设置初始值。如图11-3所示(稍后将实现向列表中添加列表项的功能)。

图11-3 收到短信后发送邀请短信

下面修改短信收发器1收到消息事件的处理程序,如果收到短信“参加快闪舞蹈团”,则将发信人手机号码添加到广播列表中。判断短信内容需要使用“如果...则...否则”块(在第10章“出题”应用中使用过),将新号码添加到列表中需要使用“向列表()添加项()”块。整个设置所需的块见表11-3。在电话号码添加完毕之后,用广播列表标签来显示新列表。

表11-3 检查来信内容,并将发信人添加到广播列表中,需要如下代码块

代码块 所在抽屉 作用
如果...则...否则 控制 对收到短信的内容进行判断,并依据判断结果执行相应操作
等于 数学 判断来信内容是否等于“参加快闪舞蹈团”
消息内容 从收到消息事件中拖出 填充到等号的左边
文本“参加快闪舞蹈团” 文本 填充到等号的右边
向列表()添加项() 列表 将申请人的电话号码添加到广播列表中
global 广播列表 从声明变量块中拖出 填充到添加列表项块的“列表”插槽中
电话号码 从收到消息事件中拖出 填充到添加列表项块的“项”插槽中
设广播列表标签的显示文本为 广播列表标签 显示新列表
global 广播列表 从声明变量块中拖出 作为广播列表标签的显示文本属性
设短信收发器1的短信为 短信收发器1 设置即将发送短信的内容
文本“恭喜你...” 文本 祝贺申请人加入群组

块的作用

如图11-4所示,要对刚收到的短信进行回复,因此,第一行代码将发信人手机号码设置为接收人手机号码,即设置短信收发器1的电话号码属性为收到消息事件中的电话号码参数。然后,判断事件中携带的消息内容参数是否为特殊代码“参加快闪舞蹈团”。如果是,则将发送者手机号添加到广播列表,并回复短信祝贺;如果不是,则回复邀请短信。在“如果...则...否则”块之后,回复短信被发出(最后一行)。

图11-4 如果收到短信“参加快闪舞蹈团”,将来信电话号码添加到广播列表

测试:用第二部手机发送短信“参加快闪舞蹈团”到测试手机,在测试手机收到短信的同时,第二部手机的号码出现在“已注册的电话号码”下面,第二部手机会收到祝贺短信。尝试发一个其他内容的短信,检查邀请短信是否能正常发送。

广播消息

下面来添加广播消息功能:当广播列表中的成员向广播中心发来短信时,将此信息转发给列表中的所有手机。这一功能略微有些复杂,需要更多的控制块:增加一个“如果...则...否则”块和一个遍历列表块。新增的“如果...则...否则”块用于检查发送短信的手机号是否在广播列表中,而遍历列表块用于向列表中的所有手机广播这条短信。另外还要将之前的“如果...则...否则”块移动到新的“如果...则...否则”块的“否则”分支中。表11-4列出了需要新增的块。

表11-4 向广播列表中的成员群发短信需要新增的块

代码块 所在抽屉 作用
如果...则...否则 控制 判断发信人是否在广播列表中,并根据判断结果执行不同操作
列表()中包含项() 列表 检查某个值是否为列表项
global 广播列表 从声明变量块中拖出 填充到“列表中包含项”块的“列表”插槽中
电话号码 从收到消息事件块中拖出 填充到“列表中包含项”块的“项”插槽中
针对列表()中的每一(项) 控制 向列表中的每个电话号码发送短信
global 广播列表 从声明变量块中拖出 填充到遍历列表块的“列表”插槽中
设短信收发器1的短信为 短信收发器1 设置短信内容
消息内容 从收到消息事件块中拖出 收到短信的内容,稍后将广播出去
设短信收发器1的电话号码为 短信收发器1 设置接收短信的电话号码
电话 从遍历列表块中拖出 遍历广播列表时,当前正在处理的项,为电话号码

块的作用

这里使用了嵌套的“如果...则...否则”块,使得程序变得更加复杂,如图11-5所示。嵌套的“如果...则...否则”块指的是在一个“如果...则...否则”块的“则”或“否则”分支中又嵌入了另一个“如果...则...否则”块。在本例中,外层的“如果...则...否则”块负责检查发信人的手机号是否已在广播列表中。如果在,则将该短信转发给列表中的所有人;如果不在,则执行内层“如果...则...否则”判断:消息内容是否为“参加快闪舞蹈团”,并依据判断结果,执行不同的分支操作。

图11-5 检查发信人是否已在广播列表中,如果是,则广播此短信

从理论上,“如果...则”块和“如果...则...否则”块可以做任意层级的嵌套,来实现更加复杂的逻辑(更多关于条件语句块的内容,请参见第18章)。

在外层“如果...则...否则”块的“则”分支中,使用遍历列表块来广播短信。通过遍历广播列表中的每一个电话号码,来设置接收短信的电话号码,并将短信发送给该号码。在遍历广播列表的过程中,列表中的每个电话号码依次被保存在占位符变量“电话”中(“电话”是一个变量,代表了遍历过程中当前正在处理的项)。在遍历列表块内,设置短信收发器1的电话号码为当前项“电话”,并向其发送短信。有关遍历列表的更多信息,请参见第20章。

测试:除了测试手机之外,还需要另外两部手机向测试手机发送“参加快闪舞蹈团”,以便注册成为广播列表成员。然后,从一部手机向广播中心发送一条短信,这时两部手机都应该收到这条短信(包括发送短信的那一个)。

美化列表的显示

广播短信的功能已经实现,但管理员的用户界面尚需改进。首先,电话号码列表显得很混乱。用标签显示列表时,列表项之间用空格分隔,并且尽可能占满一行,像下面这样:

(+861303318989 +861581235590 +8618902018909 +8613301103355 +8613801237890)

为了改善这种局面,使用表11-5列出的块创建一个名为“显示广播列表”的过程,来实现每行只显示一个号码。请务必在添加列表项块的下面调用该过程,以便显示更新后的列表。

表11-5 改进电话号码列表显示所需的块

代码块 所在抽屉 作用
定义过程()执行 过程 创建一个过程(注意不要选择“定义过程()返回”)
设广播列表标签的显示文本为 广播列表标签 显示广播列表
文本“” 文本 清空广播列表标签中的原有内容
针对列表()中的每一(项) 控制 遍历所有电话号码
global 广播列表 从声明变量块中拖出 填充到遍历列表块的“列表”插槽中
设广播列表标签的显示文本为 广播列表标签 每遍历到一个列表项时,修改标签的显示内容
拼字串 文本 将多个文本片断合并成一段文本
广播列表标签的显示文本 广播列表标签 每遍历到一个列表项时,在现有显示文本基础上添加新内容
文本“\n” 文本 添加换行符,以便下一个电话号码在下一行显示
电话 从遍历列表块中拖出 列表中正在被访问的列表项

块的作用

“显示广播列表”过程里的遍历列表块逐项地将每个手机号码添加到标签的末尾,如图11-6所示,用换行符(\n)来分隔每个号码,使得每个号码各占一行。

图11-6 分行显示电话号码

不过,“显示广播列表”过程不会主动做任何事情,除非调用它。在短信收发器1的收到消息事件中,在添加列表项块之后调用它。过程的调用取代了对广播列表的直接显示。在过程抽屉中,可以找到“调用显示广播列表”块。

图11-7显示了对收到消息事件的处理。

图11-7 调用显示广播列表过程

关于用遍历列表项的方法来显示列表内容的详细信息,请参见第20章;关于创建和调用过程的详细信息,请参见第21章。

测试:重新启动应用来清除列表,然后用至少两部不同的手机重新进行注册。手机号码是否分行显示了?

记录广播过的短信

在收到短信并向其他手机发出广播之后,程序应该记录此类事件,以便管理员可以对活动进行监督。已经在设计视图中添加的“活动日志标签”就是用于这一目的。下面编写程序,每当收到并广播新的短信时,改变活动日志标签的显示内容。

要创建像这样的一段文本:“来自+8613901231234的短信已经广播。”字符“+8613901231234”不是固定数据,而是收到消息事件中携带的参数。因此,要创建的文本包括三个部分:①“来自”;②手机号码,为事件携带的手机号码参数;③“的短信已经广播”。

正如在前几章中所做的一样,用拼字串块将三个部分连接起来,表11-6中列出了所需要的块。

表11-6 构建活动日志所需要的块

代码块 所在抽屉 作用
设活动日志标签的显示文本为 活动日志标签 显示日志内容
拼字串 文本 组合多个文本片断
文本“来自” 文本 第一部分待合并的文本片断
电话号码 从收到消息事件块中拖出 第二部分待合并的内容(动态)
文本“的短信已经广播 \n” 文本 第三部分待合并的文本片断,包含换行符\n
活动日志标签的显示文本 活动日志标签 在原有内容基础上添加新内容

块的作用

在收到短信后,向广播列表中的所有号码广播此短信,再修改活动日志标签,记录刚才的广播操作,如图11-8所示。需要注意的是,我们将消息添加到列表的开始,而不是结尾,因此最后发出的消息将显示在最顶端。

图11-8 向活动日志中添加新内容

拼字串块创建了一条新记录:来自+8613901231234的短信已经广播。

每次短信广播之后,这样的一条记录就被添加到活动日志标签的第一行,使最新的记录一直出现在顶部。拼字串块中各个文本片段的顺序决定了日志中记录的顺序。在本例子中,新消息被编排在前三个插槽中,而活动日志标签的显示文本,即已经保存的现有记录,则放在最后一个插槽中。

字符串“的短信已经广播。\n”中的“\n”称为换行符,它让每条记录单独占一行,像这样:

来自+8613030123668的短信已经广播。 来自+8613901231234的短信已经广播。

关于使用遍历列表块来显示列表内容的详细信息,请参见第20章。

将广播列表保存到数据库中

现在应用算是大功告成了,但通过前几章的学习,你可能已经猜到,这里存在一个问题:如果管理人员将应用关闭再重新打开时,广播列表中的数据将全部消失,每个人都必须重新注册。为了解决这个问题,需要使用本地数据库组件,来保存并提取广播列表中的数据。

这里将使用与“出题”应用(第10章)中相似的方案:

  • 每次添加新项时,将列表保存到数据库中;
  • 应用启动时,从数据库中加载列表,并保存到一个变量中。

用表11-7中所列出的块,将列表存储到数据库中。本地数据库组件中的参数数据标记作为数据的标识,将保存在数据库中的不同数据区分开来。在本例中,你可以将数据标记设为“广播列表”。在短信收发器1的收到消息事件中,将这些块放在添加列表项块之下。

表11-7 用本地数据库来存储广播列表所需要的块

代码块 所在抽屉 作用
让本地数据库1保存数据 本地数据库1 将数据保存到本地数据库中
文本“广播列表” 文本 填充到保存数据块的“标记”插槽中
global 广播列表 从声明变量块中拖出 填充到保存数据块的“数值”插槽中

块的作用

当应用收到短信“参加快闪舞蹈团”,并将新成员的手机号码添加到列表时,调用本地数据库1的保存数据功能,将广播列表保存到数据库中。标记(“广播列表”)的使用是数据存储的需要,也是之后从数据库提取数据的需要。如图11-9所示,保存数据块的另一个参数“数值”则是广播列表变量。

图11-9 将广播列表保存到本地数据库中

从数据库中加载广播列表

每次应用启动时,从数据库中加载广播列表,表11-8中列出了实现这一功能需要的块。

表11-8 应用启动时加载广播列表所需要的块

代码块 所在抽屉 作用
当Screen1初始化时 Screen1 应用启动时触发该事件
让本地数据库1请求数据 本地数据库1 从本地数据库中请求数据
文本“广播列表” 文本 填充到请求数据块的标记插槽中
调用显示广播列表 过程 数据加载成功后显示数据

应用的启动将触发Screen1的初始化事件,因此数据的加载要在该事件的处理程序中实现。

块的作用

应用启动时,将触发Screen1的初始化事件。如图11-10所示,使用“让本地数据库1请求数据”块向数据库请求数据。

图11-10 从数据库中加载广播列表

在使用本地数据库组件向数据库请求数据时,使用的标记参数与保存数据时的标记(广播列表)相同。一般情况下,将返回此前保存的电话号码列表,并将列表保存到全局变量广播列表中。不过,请求数据块还提供了另外一个参数插槽:“无标记返回”,用来定义当请求的标记不存在时的返回值。例如,当第一次启动应用时,在这种情况下,则返回一个空列表。

测试:你可以使用实时测试功能,来测试那些涉及数据库更新的应用{![较早前的App Inventor版本,凡是涉及本地数据库更新的应用,都无法进行实时测试。因为每次连接测试设备时,都会清空数据库。——译者注]},但要小心操作。在这个例子中,使用其他手机向测试手机发送短信,将手机号码添加到广播列表中,然后重新启动应用。这里告诉你一个重新启动应用的方法:在设计视图中,随便修改某个组件的属性,如改变标签的字体等属性。不过要全面测试数据库应用,还需要将应用打包,下载并安装到手机上(点击“编译→显示二维码或下载到本地“)。在手机上启动应用,用其他手机发送短信加入群组,再退出应用。当重启应用时,如果那些电话号码还在,说明数据库部分工作正常。

完整的“广播中心”应用

图11-11中列出了“广播中心”应用中的全部代码块。

图11-11 完整的“广播中心”应用

改进

在庆祝完成一个如此复杂的应用时,你也许还想做进一步的完善。举例如下。

  • 在广播短信环节,广播中心向所有人发出短信,也包括发送这条短信的列表成员。修改此功能,将短信群发给除了发送者之外的所有成员。
  • 允许列表成员退出群组,用手机发送短信“退出”给广播中心,请求从列表中删除自己。这项功能需要使用列表的删除指定项功能。
  • 管理人员可以在操作界面上添加或删除广播列表中的成员(手机号)。
  • 管理人员可以指定某些不允许加入列表的手机号。
  • 细化应用的功能,让任何人都可以加入列表并接收广播,但只有管理员可以广播消息。
  • 进一步细化应用,让任何人都可以加入列表并接收广播,但只有一个固定列表中的电话号码可以向全体成员广播消息。

小结

以下是本章涵盖的内容。

  • 应用不仅可以响应用户发起的事件,也可以响应非用户发起的事件,比如收到短信这样的事件。这意味着你可以开发这样的应用,应用的用户是其他手机的使用者。
  • 使用嵌套的“如果...则...否则”块以及遍历列表块,可以构造出复杂的逻辑。有关条件语句和循环语句的详细信息,请分别参见第18章及第20章。
  • 使用拼字串块可以创建一个由多项内容组成的文本对象。
  • 本地数据库组件可以用于数据库操作:存储及提取数据。最常用的方法是,在数据发生变化时,调用保存数据功能更新数据库;在应用启动时,调用请求数据功能从数据库中读取数据。