第 11 章 廣播中心

2018-02-24 15:51 更新

FrontlineSMS 是一款工具軟件,用于聯(lián)絡(luò)那些無法訪問互聯(lián)網(wǎng)但可以用手機(jī)通信的人,通常用于互聯(lián)網(wǎng)尚未普及地區(qū)的選舉監(jiān)督、天氣預(yù)報廣播等。軟件作者Ken Banks借助于移動通信技術(shù)為人們提供幫助,他的貢獻(xiàn)大概無人能及。

FrontlineSMS運行在連接了手機(jī)的電腦上。電腦和手機(jī)共同構(gòu)成一個短信中轉(zhuǎn)站,為群內(nèi)人員提供文本通信服務(wù)。無法上網(wǎng)的人可以發(fā)送一個特殊代碼來加入群,隨后他們會收到來自中轉(zhuǎn)站的各種廣播消息。這個中轉(zhuǎn)站我們稱之為“廣播中心”,對于那些沒有網(wǎng)絡(luò)的地方,廣播中心成為與外界聯(lián)系的重要手段。

使用App Inventor可以創(chuàng)建自己的短信處理應(yīng)用。有趣的是,應(yīng)用需要運行在一部android設(shè)備上,但應(yīng)用的用戶卻不必使用Android手機(jī),他們可以用任何手機(jī),智能的或非智能的,與應(yīng)用之間進(jìn)行短信的交流。應(yīng)用雖然具有圖形化的用戶界面(GUI),但GUI僅供應(yīng)用的管理者使用,用來監(jiān)控應(yīng)用中的各種活動。

{%}

本章將創(chuàng)建一個與FrontlineSMS功能類似的廣播中心,不過是運行在Android手機(jī)上。一臺具有中轉(zhuǎn)樞紐作用的移動設(shè)備,意味著管理者可以在移動中保持交流,這一點在某些場合下尤其重要,如選舉監(jiān)督和醫(yī)療爭議談判。

假想有一個“快閃舞蹈團(tuán)”(FlashMob Dance Team,縮寫為FMDT),他們可以召之即來,隨時隨地表演舞蹈,然后瞬間解散,消失得無影無蹤,他們用你創(chuàng)建的廣播中心來組織表演活動。人們只要向中心發(fā)送短信“joinFMDT”(參加快閃舞蹈團(tuán)),即可完成入團(tuán)注冊,每個注冊成功的人都可以向舞蹈團(tuán)中的其他人廣播消息。

廣播中心用下面的方式處理收到的短信:

1. 如果發(fā)信人不在廣播中心的成員名單中,則回復(fù)短信邀請他加入,并告知他申請代碼;

2. 如果收到“joinFMDT”,則接收發(fā)信人為廣播中心成員;【如果組員發(fā)送“joinFMDT”呢?】

3. 如果發(fā)信人已經(jīng)是廣播中心的成員,則轉(zhuǎn)發(fā)該消息給全體廣播中心成員。

我們來分步實現(xiàn)這些功能模塊。首先,用自動回復(fù)來邀請人們加入廣播中心。整個應(yīng)用完成之后,對于創(chuàng)建這類“以短信為用戶界面的應(yīng)用”,你將有透徹的了解。

學(xué)習(xí)要點

本章包括下列App Inventor概念,其中有些你可能已經(jīng)熟悉了:

  • Texting組件:發(fā)送短信及處理收到的短信;

  • 列表變量:在本例中用來記錄電話號碼清單;

  • foreach塊:對列表中的數(shù)據(jù)進(jìn)行逐項重復(fù)操作。在本例子中,使用foreach塊向電話號碼列表中的所有手機(jī)廣播消息;

  • TinyDB組件:實現(xiàn)數(shù)據(jù)的永久存儲,以保證當(dāng)應(yīng)用關(guān)閉并再次打開時,電話號碼列表不丟失。

準(zhǔn)備開始

你需要一部可以接收和發(fā)送短信的手機(jī)來測試程序,因為App Inventor自帶的模擬器沒有這個功能。您還需要招呼一些朋友給你發(fā)送短信,來充分地測試應(yīng)用。

連接到App Inventor網(wǎng)站,創(chuàng)建新項目“BroadcastHub”,設(shè)置Screen1.Title屬性為“廣播中心”,并連接測試手機(jī)。

設(shè)計組件

廣播中心有利于手機(jī)之間的通信:這些手機(jī)不需要安裝應(yīng)用,甚至不必是智能手機(jī)。因此在本例中不必為用戶提供操作界面,只需為群管理員提供操作界面。

管理員的操作界面包括兩個簡單的部分,一是顯示當(dāng)前的“廣播列表”,即已注冊成員的電話號碼清單,二是記錄所有收到并被廣播出去的短信。

為了創(chuàng)建這個界面,要添加表11-1中列出的組件。

表11-1 廣播中心操作界面中的組件

組件類型 面板中分組 命名 作用
Label User Interface Label1 電話號碼清單的標(biāo)題
Label User Interface BroadcaseListLabel 顯示所有已注冊的電話號碼
Label User Interface Label2 日志信息的標(biāo)題
Label User Interface LogLabel 顯示收到及廣播短信的記錄
Texting Social Texting1 處理短信
TinyDB Storage TinyDB1 保存已注冊的手機(jī)號碼清單

添加組件之后,還要設(shè)置以下屬性:

1. 設(shè)置每個Label的Width屬性為“Fill parent”,讓組件在水平方向上充滿手機(jī);

2. 設(shè)置標(biāo)題Label的FontSize屬性(Label1和Label2)為18,并勾選FontBold框;

3. BroadcastListLabel和LogLabel的Height設(shè)置為200像素,用于顯示多行;

4. 設(shè)置BroadcastListLabel的Text屬性為“廣播列表...”;

5. LogLabel的Text屬性設(shè)置為空。

圖11-1顯示了應(yīng)用在組件設(shè)計器中的布局。

{%}

圖 11-1 廣播中心組件設(shè)計

為組件添加行為

在這個應(yīng)用中,促使程序運行的事件是其他手機(jī)發(fā)來的短信,而不是用戶在界面上的輸入或點擊,因此應(yīng)用的任務(wù)是處理這些短信,并將發(fā)信人手機(jī)號碼保存到列表中,具體操作如下:

  • 如果短信發(fā)送者不在廣播列表中,則回復(fù)一個邀請參加的短信;

  • 如果收到短信“joinFMDT”,則將發(fā)送者注冊為廣播列表的一員;

  • 如果短信發(fā)送者已經(jīng)在廣播列表中,則將該短信廣播到列表中的所有手機(jī)。

現(xiàn)在開始創(chuàng)建第一個行為:收到短信時,回復(fù)發(fā)送者,邀請他注冊,方法是向你發(fā)送短信“joinFMDT”。表11-2中列出了需要的塊。

表11-2 邀請人們通過發(fā)短信來加入群組,需要下面的塊

塊的類型 所在抽屜 作用
Texting.MessageReceived Texting1 當(dāng)手機(jī)收到短信時,觸發(fā)該事件
set Texting1.PhoneNumber to Texting1 設(shè)置短信接收者的電話號碼
參數(shù)number Variables MessageReceived事件的參數(shù):發(fā)送者手機(jī)號
Set Texting1.Message Texting1 設(shè)置要發(fā)送的邀請短信
“想加入快閃舞蹈團(tuán),請發(fā)送‘joinFMDT’到此號碼?!?/td> Text 邀請短信的內(nèi)容 Texting1.SendMessage Texting1 發(fā)送短信

塊的作用

根據(jù)在第4章“開車不發(fā)短信”中的經(jīng)驗,你應(yīng)該很熟悉這些塊。當(dāng)手機(jī)收到短信時會觸發(fā)Texting1.MessageReceived事件。如圖11-2,在事件處理程序中設(shè)置Texting1組件的PhoneNumber及Message屬性,然后發(fā)送短信。

{%}

圖 11-2 收到短信后回復(fù)邀請短信

 測試:需要用第二部手機(jī)來測試這一功能;你不能給自己發(fā)短信,否則會永遠(yuǎn)循環(huán)下去!如果沒有其他手機(jī),可以注冊Google Voice或類似的服務(wù),從這些服務(wù)中給自己的手機(jī)發(fā)短信。用第二部手機(jī)發(fā)送“你好”到測試手機(jī),則第二部手機(jī)會收到一個邀請加入“舞蹈團(tuán)”的短信。

將某人加入廣播列表

現(xiàn)在創(chuàng)建第二個行為:收到短信“joinFMDT”后,將發(fā)信人添加到廣播列表中。首先定義列表變量BroadcastList來保存注冊的電話號碼。從Variables中拖出一個“initialize global name to”塊,將name改為“BroadcastList”,并用make a list塊初始化列表,此時列表為空。如圖11-3(稍后將實現(xiàn)向列表中添加數(shù)據(jù)項的功能)。

{%}

圖 11-3 變量BroadcastList用于存儲注冊的電話號碼【也可用create empty list塊】

下面修改Texting1.MessageReceived事件處理程序,如果收到短信“joinFMDT”,則將發(fā)信人手機(jī)號碼添加到BroadcastList中。判斷短信內(nèi)容需要使用Ifelse塊(在第十章“出題”應(yīng)用中使用過),將新號碼添加到列表中需要使用add item to list塊。整個設(shè)置所需的塊見表11-3。在電話號碼添加完之后,用BroadcastListLabel來顯示新列表。

表11-3 檢查來信內(nèi)容,并將發(fā)信人添加到廣播列表中,需要如下塊

塊的類型 所在抽屜 作用
initialize global BroadcastList to Variables 定義廣播列表變量
ifelse Control 根據(jù)收到短信的內(nèi)容決定做什么事
= Math 判斷短信內(nèi)容是否等于“joinFMDT”
get messageText Variables 將來信內(nèi)容插入“=”塊(左邊
“joinFMDT” Text 將固定文本插入“=”塊(右邊)
add items to list Lists 向廣播列表中添加發(fā)信人電話號
get number Variables 將發(fā)信人手機(jī)號碼插入“add items to list”
set BroadcaseListLabel.Text to BroadcaseListLabel 顯示新列表
get global BroadcastList Variables 將其插入set BroadcaseListLabel.Text to塊
set Texting1.Message to Texting1 設(shè)置短信內(nèi)容,準(zhǔn)備用Texting1回復(fù)發(fā)信人
“恭喜你成功加入…” Text 祝賀發(fā)信人加入群組成功。

塊的作用

如圖11-4所示,對剛收到的短信進(jìn)行回復(fù),第一行的塊將發(fā)信人手機(jī)號碼設(shè)置為接收人手機(jī)號碼,即設(shè)置Texting1.PhoneNumber為number。然后判斷messageText是否為特殊代碼“joinFMDT”:如果是,則將發(fā)送者手機(jī)號添加到BroadcastList并發(fā)短信祝賀;如果不是,則回復(fù)邀請短信。在Ifelse塊之后,回復(fù)短信被發(fā)出(最后一行)。

{%}

圖 11-4 如果收到短信“joinFMDT”,則將發(fā)信人手機(jī)號添加到BroadcastList

 測試:用第二部手機(jī)發(fā)送短信“joinFMDT”到測試手機(jī),在測試手機(jī)收到短信的同時,第二部手機(jī)的號碼出現(xiàn)在“已注冊的電話號碼”下面,第二部手機(jī)會收到祝賀短信。嘗試發(fā)一個其他內(nèi)容的短信,檢查邀請短信是否能正常發(fā)送。

廣播消息

下面來添加廣播行為:當(dāng)廣播列表BroadcastList中的成員向廣播中心發(fā)來短信時,將此信息轉(zhuǎn)發(fā)給列表中的所有手機(jī)。這一功能稍顯復(fù)雜,需要更多的控制塊:增加一個Ifelse塊和一個foreach塊。新增的Ifelse塊用于檢查發(fā)送短信的手機(jī)號是否在廣播列表中,而foreach塊用于向列表中的所有手機(jī)廣播這條短信。另外還要將之前的Ifelse塊移動到新Ifelse塊的“else”部分。表11-4列出了需要新增的塊。

表11-4 向列表中的成員廣播某個成員發(fā)來的短信需要新增的塊

塊的類型 所在抽屜 作用
ifelse Control 根據(jù)發(fā)信人是否已在廣播列表中來決定做不同的事
is in list? Lists 檢查某數(shù)據(jù)是否在列表中
get global BroadcastList Variables 將其插入is in list?的list插槽中
get number Variables 將其插入is in list?的thing插槽
set Texting1.Message to Texting1 設(shè)置將被廣播出去的短信內(nèi)容(列表成員的來信)
get messageText Variables 即將被廣播出去的列表成員來
foreach Control 向列表中的所有成員發(fā)送同一條短信
get global BroadcastList Variables 將其插入foreach的list插槽
set Texting1.PhoneNumber to Texting1 設(shè)置接收短信的手機(jī)號碼
get item Variables BroadcaseList中當(dāng)前正在操作的項/變量:保存的是手機(jī)號

塊的作用

這里使用了嵌套的ifelse塊,使得程序更加復(fù)雜,如圖11-5所示。嵌套的ifelse塊指的是在一個ifelse塊的“then”或“else”插槽中嵌入了另一個ifelse塊。在本例中,外層的ifelse負(fù)責(zé)檢查發(fā)信人的手機(jī)號是否已在廣播列表中。如果在,則將該短信轉(zhuǎn)發(fā)給列表中的所有人;如果不在,則執(zhí)行內(nèi)層ifelse判斷:短信內(nèi)容messageText是否為“joinFMDT”,并依據(jù)判斷結(jié)果,執(zhí)行不同的分支操作。

{%}

圖 11-5 檢查發(fā)信人是否已在廣播列表中,如果是,則廣播此短信

從理論上,if塊和ifelse塊可以做任意層級的嵌套,來實現(xiàn)更加復(fù)雜的行為(更多關(guān)于條件語句塊的內(nèi)容請參見第18章)。

在外層ifelse塊的then分支中,使用foreach塊來廣播短信。foreach遍歷BroadcastList列表中的每一項,并把短信發(fā)送給列表中的每個電話號碼。在foreach執(zhí)行循環(huán)時,BroadcastList中的每個電話號碼依次被保存在item中(item是一個變量,代表了foreach當(dāng)前正在處理的項)。在foreach塊內(nèi),設(shè)置Texting.PhoneNumber的值為當(dāng)前項item,并向其發(fā)送短信。有關(guān)foreach的更多信息,請參見第20章。

 測試:首先要有兩部不同的手機(jī)通過發(fā)送“joinFMDT”到測試手機(jī),實現(xiàn)成功注冊。然后,從一部手機(jī)向廣播中心發(fā)一條短信,這時兩部手機(jī)都應(yīng)該收到這條短信(包括發(fā)送短信的那一個)。

整理列表的顯示

廣播短信的功能已經(jīng)實現(xiàn),但管理員的界面尚需改進(jìn)。首先,電話號碼列表的顯得很亂:用Label顯示列表時,列表項之間用空格分隔,并且盡可能占滿一行,像下面這樣:

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

為了改善這種局面,使用表11-5列出的塊創(chuàng)建一個過程displayBroadcastList,來實現(xiàn)每行只顯示一個號碼。請務(wù)必在add items to list塊的下面調(diào)用該過程,以便顯示更新后的列表。

表11-5 改進(jìn)電話號碼列表顯示所需的塊

塊的類型 所在抽屜 作用
to procedure(“displayBroadcastList”) Procedures 創(chuàng)建過程displayBroadcastList
set BroadcaseListLabel.Text to BroadcaseListLabel 用來顯示列表
“” Text 空文本
foreach Control 對電話號碼列表進(jìn)行遍歷
pnumber foreach內(nèi)置 變量pnumber為遍歷過程中正在訪問的
get global BroadcaseList Variables 插入foreach塊的in list插槽
set BroadcaseListLabel.Text to BroadcaseListLabel 顯示電話號碼列表
join Text 將多個文本片段連接為一個文本對象
BroadcaseListLabel.Text BroadcaseListLabel 每次循環(huán)都以既有l(wèi)abel內(nèi)容為基礎(chǔ)追加新項
“\n” Text 換行,以便下一個號碼顯示在下一行
get pnumber foreach內(nèi)置 遍歷時列表中正在訪問的項(手機(jī)號碼)

塊的作用

過程displayBroadcastList中的foreach塊逐行地將每個手機(jī)號碼添加到label的末尾,如圖11-6所示,用換行符(\ n)來分隔每個號碼,使得每個號碼各占一行。

{%}

圖 11-6 逐行顯示手機(jī)號碼

不過displayBroadcastList過程不會主動做任何事,除非調(diào)用它。在Texting1.MessageReceived事件處理程序中,緊接著add item to list塊調(diào)用它。過程的調(diào)用取代了列表BroadcastList在 BroadcastListLabel.Text中的默認(rèn)顯示。塊call displayBroadcastList歸屬在Procedures抽屜中。

圖11-7顯示了Texting1.MessageReceived事件處理程序中相關(guān)的塊。

{%}

圖 11-7 調(diào)用displayBroadcastList過程

關(guān)于用foreach來顯示列表的詳細(xì)信息請參見第20章,關(guān)于創(chuàng)建和調(diào)用過程的詳細(xì)信息請參見第21章。

 測試:重新啟動應(yīng)用來清除列表,然后用至少兩個不同的手機(jī)進(jìn)行注冊(再次)。手機(jī)號碼是否逐行顯示了?

錄廣播過的短信

在收到短信并向其他手機(jī)發(fā)出廣播之后,程序應(yīng)該記錄此類事件,以便管理員可以對活動進(jìn)行監(jiān)督。在組件設(shè)計器中,已經(jīng)添加的LogLabel組件就是用于這一目的。下面編寫程序,每當(dāng)收到新的短信時,改變LogLabel的顯示。

要創(chuàng)建像這樣的一段文本:“來自+8613901231234的短信已經(jīng)廣播。”字符“+8613901231234”不是固定數(shù)據(jù),而是MessageReceived事件自帶的參數(shù)值。因此,要創(chuàng)建的文本包括三個部分:①“來自”;②手機(jī)號碼,為參數(shù)number;③“的短信已經(jīng)廣播”。正如在前幾章中所做的一樣,用join將三個部分連接起來,表11-6列出了需要的塊。

表11-6 構(gòu)建廣播日志所需要的塊

塊的類型 所在抽屜 作用
set LogLabel.Text to LogLabel 在此顯示日志
join Text 由多個文本片段創(chuàng)建成一個文本對象
“來自” Text 每條日志信息的第①部
get number Texting1.MessageReceived事件內(nèi)置參數(shù) 日志信息的第②部分:短信發(fā)送者的手機(jī)號碼
“的短信已經(jīng)廣播。\n” Text 日志信息的第③部分
LogLabel.Text LogLabel 在原有日志前插入一條新的日志

塊的作用

在收到短信后,向BroadcastList列表中的所有號碼廣播此短信,再修改LogLabel,記錄剛才的廣播操作,如圖11-8所示。需要注意的是,我們將消息添加到列表的開始,而不是結(jié)尾,因此最后發(fā)出的消息將顯示在最頂端。

{%}

圖 11-8 向廣播日志中添加一條新消息

join塊創(chuàng)建了一條新記錄:來自+8613901231234的短信已經(jīng)廣播。

每次短信廣播之后,這條記錄將被添加到LogLabel.Text的第一行,使最新的記錄一直出現(xiàn)在頂部。join塊中各個文本片段的順序決定了日志中記錄的順序。在本例子中,新消息被編排在前三個插槽中,而LogLabel.Text,已經(jīng)保存的現(xiàn)有記錄,將插入最后一個插槽。

“的短信已經(jīng)廣播。\n”中的“\n”稱為換行符,它讓每條記錄單獨占一行,像這樣:

來自+8613030123668的短信已經(jīng)廣播。

來自+8613901231234的短信已經(jīng)廣播。

關(guān)于使用foreach來顯示列表的詳細(xì)信息,請參見第20章。

將BroadcastList保存在數(shù)據(jù)庫中

現(xiàn)在應(yīng)用算是大功告成了,但通過前幾章的學(xué)習(xí),你可能猜到了一個問題:如果管理員將應(yīng)用關(guān)閉再重新啟動時,廣播列表中的數(shù)據(jù)將會丟失,每個人都得重新注冊。為了解決這個問題,要使用TinyDB組件實現(xiàn)BroadcastList列表在數(shù)據(jù)庫中的存儲和檢索。

這里將使用與“出題”應(yīng)用(第10章)中相類似的方案:

每次添加新項時,將列表保存到數(shù)據(jù)庫中;

應(yīng)用啟動時,從數(shù)據(jù)庫中加載列表,并保存到一個變量中。

用表11-7中所列的塊,將列表存儲到數(shù)據(jù)庫中。TinyDB組件中的tag作為數(shù)據(jù)的標(biāo)識,將保存在數(shù)據(jù)庫中的不同數(shù)據(jù)區(qū)分開來。在本例中,你可以將數(shù)據(jù)標(biāo)記為“broadcastList”。在Texting1.MessageReceived中,將這些塊添加到add items to list塊之下。

表11-7 用TinyDB來存儲列表所需的塊

塊的類型 所在抽屜 作用
TinyDB1.StoreValue TinyDB1 將數(shù)據(jù)保存到數(shù)據(jù)庫中
“broadcastList” Text 將其插入StoreValue的tag插槽中
get global BroadcastList Variables 將其插入StoreValue的value插槽中

塊的功能

當(dāng)應(yīng)用收到短信“joinFMDT”,并將新成員的手機(jī)號碼添加到列表時,調(diào)用TinyDB1.StoreValue將BroadcastList保存到數(shù)據(jù)庫中。tag(“broadcastList”)的使用是為了便于之后對數(shù)據(jù)的檢索。如圖11-9,被StoreValue調(diào)用的值(valueToStore)是變量BroadcastList。

{%}

圖 11-9 調(diào)用TinyDB來存儲BroadcastList列表

從數(shù)據(jù)庫加載廣播列表(BroadcastList)

每次應(yīng)用啟動時都要加載廣播列表,按照表11-8中列出的塊來實現(xiàn)這一功能。應(yīng)用的啟動將觸發(fā)Screen1.Initialize事件,因此將在該事件的處理程序中實現(xiàn)加載。使用存儲時的tag(“broadcastList”)來調(diào)用TinyDB.GetValue。就像前幾章一樣,我們需要檢查是否的確有數(shù)據(jù)返回,這里將檢查返回值是否為列表,因為如果列表中沒有數(shù)據(jù),那么它也就不是列表。

塊的作用

應(yīng)用啟動將觸發(fā)Screen1.Initialize事件。如圖11-10所示,使用TinyDB1.GetValue塊向數(shù)據(jù)庫請求數(shù)據(jù),返回的數(shù)據(jù)臨時保存在已定義的變量valueFromDB中。

表11-8 應(yīng)用啟動時加載廣播列表所需要的塊

塊的類型 所在抽屜 作用
initialize global valueFromDB to Variables 用于保存并檢查數(shù)據(jù)庫返回值的臨時變量
“” Text 設(shè)valueFromDB初始值為空
Screen1.Initialize Screen1 應(yīng)用啟動時觸發(fā)該事件
set global valueFromDB to Variables 將數(shù)據(jù)庫返回值暫時存放在其中
TinyDB1.GetValue TinyDB1 向數(shù)據(jù)庫請求數(shù)據(jù)
“broadcastList” Text 將其插入GetValue的tag插槽
if Control 判斷數(shù)據(jù)庫中是否有數(shù)據(jù)
is a list Lists 如果數(shù)據(jù)庫返回值是一個列表,則返回值不為空
get global valueFromDB Variables 將其插入is a list?
set global BroadcaseList to Variables 將變量值設(shè)置為數(shù)據(jù)庫的返回值
get global valueFromDB Variables 數(shù)據(jù)庫返回值不為空時,將返回值寫入廣播列表
call displayBroadcastList Procedures 加載數(shù)據(jù)成功后,顯示數(shù)據(jù)

{%}

圖 11-10 從數(shù)據(jù)庫中加載廣播列表BroadcastList

事件處理程序中的if塊是必需的,因為在首次啟動應(yīng)用時,數(shù)據(jù)庫將返回空文本(“”),這時還沒有生成廣播列表。通過判斷valueFromDB是否為列表,可以確定是否真的有數(shù)據(jù)返回。如果沒有,則跳過那些保存返回數(shù)據(jù)以及顯示數(shù)據(jù)的塊。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號