Facebook的數(shù)據(jù)庫(kù)中,有每位用戶的賬戶信息、好友列表以及發(fā)布的信息,Amazon的數(shù)據(jù)庫(kù)中有你能買到的任何東西,而Google的數(shù)據(jù)庫(kù)中有互聯(lián)網(wǎng)上的每個(gè)頁(yè)面的信息。你自己的應(yīng)用雖然沒有那么大的規(guī)模,但一個(gè)正規(guī)的應(yīng)用都會(huì)用到數(shù)據(jù)庫(kù)組件。
在大多數(shù)的編程環(huán)境中,編寫與數(shù)據(jù)庫(kù)通信的應(yīng)用是一種高級(jí)編程技術(shù):要搭建數(shù)據(jù)庫(kù)(軟件)服務(wù)器,如Oracle或MySQL等,并編寫程序與數(shù)據(jù)庫(kù)建立連接。在大學(xué)里,這些內(nèi)容通常要在軟件工程或數(shù)據(jù)庫(kù)這樣的高級(jí)課程中才會(huì)涉及。
App Inventor承擔(dān)了與數(shù)據(jù)庫(kù)(以及許多其它有用的事情)有關(guān)的這部分繁瑣的設(shè)置,在這個(gè)語(yǔ)言中,提供了數(shù)據(jù)庫(kù)組件,將數(shù)據(jù)庫(kù)通信簡(jiǎn)化為單純的讀寫操作。應(yīng)用可以直接將數(shù)據(jù)保存在Android設(shè)備上,也可以保存到集中式網(wǎng)絡(luò)數(shù)據(jù)庫(kù)中,從而實(shí)現(xiàn)在不同設(shè)備與其他人之間的數(shù)據(jù)共享。
保存在變量及組件屬性中的數(shù)據(jù)屬于臨時(shí)存儲(chǔ):如果用戶在表單中輸入某些信息然后關(guān)閉應(yīng)用,那么當(dāng)應(yīng)用重新打開時(shí),這些信息將不復(fù)存在。想要長(zhǎng)期保存信息,就需要將它們保存到數(shù)據(jù)庫(kù)中。數(shù)據(jù)庫(kù)中的信息被稱為永久信息,因?yàn)楫?dāng)應(yīng)用在關(guān)閉后重新打開時(shí),數(shù)據(jù)依然存在。
作為例子,考慮第4章開車不發(fā)短信的應(yīng)用,那個(gè)繁忙時(shí)自動(dòng)回復(fù)短信的應(yīng)用。這個(gè)應(yīng)用允許用戶輸入一條個(gè)性化的信息,作為收到短信時(shí)的自動(dòng)回復(fù)信息。如果用戶將信息改為“我在睡覺,別來(lái)煩我”,然后關(guān)閉了應(yīng)用,當(dāng)重新打開應(yīng)用時(shí),定制的自動(dòng)回復(fù)信息依然是“我在睡覺,別來(lái)煩我”。因此,定制信息必須保存到數(shù)據(jù)庫(kù)中,在每次啟動(dòng)應(yīng)用時(shí),再將信息從數(shù)據(jù)庫(kù)提取到應(yīng)用中。
App Inventor提供了兩個(gè)便于操作數(shù)據(jù)庫(kù)的組件:TinyDB及TinyWebDB。TinyDB用于直接在Android設(shè)備上永久保存數(shù)據(jù),它適合于那些極其私人化的應(yīng)用,如開車不發(fā)短信,這類應(yīng)用不需要讓數(shù)據(jù)在不同設(shè)備及人群之間共享。而TinyWebDB則將數(shù)據(jù)保存到web數(shù)據(jù)庫(kù)中,并可實(shí)現(xiàn)不同設(shè)備之間的共享。能夠通過(guò)web數(shù)據(jù)庫(kù)訪問(wèn)數(shù)據(jù),這是多人游戲及應(yīng)用的基礎(chǔ),用戶可以借此分享信息(如第10章的出題應(yīng)用)。
這兩個(gè)數(shù)據(jù)庫(kù)組件非常相似,但TinyDB更簡(jiǎn)單些,因此我們先來(lái)研究它。首先,不需要任何設(shè)置就可以直接使用它,此外,數(shù)據(jù)直接保存在設(shè)備上,并于應(yīng)用相關(guān)聯(lián)。
使用TinyDB.StroeValue塊來(lái)實(shí)現(xiàn)數(shù)據(jù)的長(zhǎng)期存儲(chǔ),如圖22-1所示,這段代碼來(lái)自于“開車不發(fā)短信”。
http://appinvtinywebdb.appspot.com的web數(shù)據(jù)庫(kù)服務(wù)器中。由于這里用的是默認(rèn)的服務(wù),會(huì)顯示來(lái)自于各種應(yīng)用的很多數(shù)據(jù),因此在第一個(gè)顯示窗口中,有可能看到,也有可能看不到你的數(shù)據(jù)。如果看不到,可以用頁(yè)面上的GetValue鏈接用特定標(biāo)簽來(lái)搜索數(shù)據(jù)。
測(cè)試:用TinyWebDB編程時(shí),使用數(shù)據(jù)庫(kù)服務(wù)器的web接口來(lái)測(cè)試是否按要求被保存起來(lái)。
用TinyWebDB提取數(shù)據(jù)要比TinyDB復(fù)雜得多。由于TinyDB的GetValue操作是直接與Android設(shè)備上的數(shù)據(jù)庫(kù)通信,因而可以立即獲得返回值,但使用TinyWebDB的應(yīng)用則需要跨越網(wǎng)絡(luò)來(lái)請(qǐng)求數(shù)據(jù),因此需要分兩步來(lái)實(shí)現(xiàn)。
首先使用TinyWebDB的GetValue請(qǐng)求數(shù)據(jù),稍后再來(lái)處理TinyWebDB.GotValue事件處理程序。實(shí)際上,TinyWebDB.GetValue應(yīng)該叫做“RequestValue(請(qǐng)求值)”,因?yàn)樗皇窍騱eb數(shù)據(jù)庫(kù)發(fā)出請(qǐng)求,而請(qǐng)求實(shí)際上并不能立即“get(得到)”一個(gè)值。為了更清楚地了解二者之間的差別,可以對(duì)比圖22-5中的TinyDB.GetValue與圖22-6中的TinyWebDB.GetValue。
圖 22-6 TinyWebDB.GetValue塊
TinyDB.GetValue塊立即得到返回值,因此該塊的左側(cè)有一個(gè)插頭以便可以將返回值保存到一個(gè)變量或?qū)傩灾?;而TinyWebDB.GetValue塊不能立即得到返回值,因此左側(cè)沒有插頭。
對(duì)TinyWebDB而言,當(dāng)web數(shù)據(jù)庫(kù)實(shí)現(xiàn)了請(qǐng)求并將數(shù)據(jù)返回給設(shè)備時(shí),將觸發(fā)TinyWebDB.GotValue事件。因此整個(gè)提取數(shù)據(jù)過(guò)程分為兩步,首先在一個(gè)地方調(diào)用TinyWebDB.GetValue,然后再編寫TinyWebDB.GotValue事件處理程序,來(lái)處理實(shí)際接收到的數(shù)據(jù)。像TinyWebDB.GotValue這樣的程序有時(shí)被稱作回調(diào)過(guò)程,因?yàn)閷?shí)際上是某些外部實(shí)體(這里是web數(shù)據(jù)庫(kù))在處理完你的請(qǐng)求之后,反過(guò)來(lái)調(diào)用你的程序。就像在一家繁忙的咖啡店點(diǎn)餐一樣:你點(diǎn)餐,然后等待咖啡師喊你的名字,你才能真正拿到你的飲料。在同一時(shí)間,咖啡師會(huì)按順序從每個(gè)人手里收取點(diǎn)餐單(而且所有人都在等待自己的名字被喊到)。
在我們的例子中,需要保存并提取一個(gè)投票者的列表,并最終顯示所有人的投票結(jié)果。
最簡(jiǎn)單的方案是在應(yīng)用啟動(dòng)時(shí),在Screen.Initialize事件中發(fā)出請(qǐng)求來(lái)提取列表數(shù)據(jù)。如圖22-7所示(在本例中,用“voterlist”為標(biāo)簽向數(shù)據(jù)庫(kù)發(fā)出請(qǐng)求。)
圖 22-8 使用TinyWebDB.GotValue事件處理程序處理返回的列表
程序GotValue附帶了參數(shù)valueFromWebDB,其中保存著向數(shù)據(jù)庫(kù)請(qǐng)求的數(shù)據(jù)。像valueFromWebDB這樣的事件附帶的參數(shù),只在該事件處理程序范圍內(nèi)有效(隸屬于該事件處理程序),因此無(wú)法在其他事件處理程序中引用該參數(shù)。
這一點(diǎn)看似有些費(fèi)解,但一旦你熟悉了這些保存局部數(shù)據(jù)的參數(shù),你自然會(huì)聯(lián)想到那些適用范圍更大的數(shù)據(jù)(在整個(gè)應(yīng)用中隨處可用):變量。理解了這一點(diǎn),也就理解了GotValue中的關(guān)鍵一步:將返回的數(shù)據(jù)valueFromWebDB轉(zhuǎn)移到一個(gè)變量中。這里是將數(shù)據(jù)轉(zhuǎn)移到變量voterList中,之后可以在其他的事件處理程序中使用該變量。
通常會(huì)在GotValue中同時(shí)使用if塊,原因是,如果數(shù)據(jù)庫(kù)中不存在被請(qǐng)求的數(shù)據(jù),則返回值為空文本(“”),通常這種情況發(fā)生在第一次啟動(dòng)應(yīng)用時(shí)。通過(guò)檢查valueFromWebDB是否為列表,可以確定是否真的有數(shù)據(jù)返回。如果valueFromWebDB為空(if的測(cè)試結(jié)果為假),就不必將其寫入變量voterList。
無(wú)論是TinyDB還是TinyWebDB,都是以相同的方式來(lái)獲取數(shù)據(jù)、檢查數(shù)據(jù)及設(shè)置數(shù)據(jù)(到變量中),不同的是,這里預(yù)期會(huì)收到一個(gè)列表,因此測(cè)試環(huán)節(jié)上略有差別。
在相對(duì)簡(jiǎn)單的應(yīng)用中,圖22-8中所示的代碼是一種不錯(cuò)的提取數(shù)據(jù)的方式,但在投票的例子中,我們需要更為復(fù)雜的邏輯。說(shuō)明如下:
應(yīng)用啟動(dòng)時(shí),程序會(huì)提示用戶輸入Email地址??梢允褂肗otifier組件彈出窗口來(lái)實(shí)現(xiàn)這一功能。(Notifier在組件設(shè)計(jì)器組件面板的User Interface中。)用戶輸入email后,將其保存為變量;
圖22-9顯示了向數(shù)據(jù)庫(kù)請(qǐng)求數(shù)據(jù)的更為復(fù)雜的方案。
圖 22-9 在這個(gè)更為復(fù)雜的方案里,在獲得用戶的email之后調(diào)用GetValue
在應(yīng)用啟動(dòng)時(shí)(Screen1.Initialize),Notifier組件提示用戶輸入他的email地址;用戶輸入后(Notifier.AfterTextInput),輸入的信息保存到變量中,同時(shí)用label顯示出來(lái),然后調(diào)用GetValue來(lái)獲得投票人列表。需要注意,這里沒有在Screen1.Initialize中直接調(diào)用GetValue,因?yàn)樾枰紫仍O(shè)置用戶的Email地址。
因此當(dāng)應(yīng)用初始化完成后,用這些塊來(lái)提示用戶的Email地址,然后以“voterlist”為標(biāo)簽調(diào)用GetValue。當(dāng)從web上返回列表時(shí),GotValue被觸發(fā),以下是后續(xù)功能的描述:
GotValue將檢查到達(dá)的數(shù)據(jù)是否不為空(有人已經(jīng)使用這個(gè)應(yīng)用,并建立了投票人列表)。如果返回值中包含數(shù)據(jù)(投票人列表),則檢查此用戶的email是否已經(jīng)在投票人列表中,如果沒有,將其添加至列表,并將更新后的列表保存到數(shù)據(jù)庫(kù);
圖22-10中顯示了這一功能所需的塊。
圖 22-10 使用GotValue塊處理數(shù)據(jù)庫(kù)返回的數(shù)據(jù),根據(jù)不同的返回結(jié)果確定要執(zhí)行的操作
在這些塊中,第一個(gè)if通過(guò)調(diào)用“is a list?”來(lái)檢測(cè)從數(shù)據(jù)庫(kù)返回的值,判斷其是否不為空。如果不為空,返回的數(shù)據(jù)放入變量voterList中。切記,voterList中只有每個(gè)使用過(guò)該應(yīng)用的用戶的Email地址,但我們不確定當(dāng)前用戶是否也在此列表中,因此需要檢查一下:如果此用戶不在列表中,則用“add item to list”塊將其添加至列表,并將更新后的列表保存到web數(shù)據(jù)庫(kù)。
如果數(shù)據(jù)庫(kù)返回的結(jié)果不是列表,則執(zhí)行ifelse塊中的“else”分支;這說(shuō)明還沒有人使用過(guò)這個(gè)應(yīng)用。此時(shí)需要?jiǎng)?chuàng)建一個(gè)新的列表voterList,將當(dāng)前用戶的Email地址作為列表的第一項(xiàng),然后將這個(gè)只有一項(xiàng)的列表保存到web數(shù)據(jù)庫(kù)中(同時(shí)也希望更多人的加入?。?。
到目前為止,投票應(yīng)用值處理了一個(gè)用戶列表,每個(gè)用戶都可以看到其他用戶的Email地址,但還不能提取并顯示每個(gè)用戶的投票結(jié)果。
此前設(shè)定在VoteButton的Click事件中,將用戶的Email地址與投票結(jié)果以“email地址:投票結(jié)果”的方式組成tag-value對(duì)提交給web數(shù)據(jù)庫(kù)。此時(shí)如果已經(jīng)有兩個(gè)人投票,那么相應(yīng)的數(shù)據(jù)庫(kù)實(shí)體中將包含表22-3中的數(shù)據(jù)。
表22-3 存儲(chǔ)在數(shù)據(jù)庫(kù)中的tag-value對(duì)
tag | value |
---|---|
voterlist | [wolver@gmail.com,joe@gmail.com] |
wolber@gmail.com | Obama |
joe@gmail.com | McCain |
當(dāng)用戶點(diǎn)擊“ViewVotes”按鈕時(shí),應(yīng)用將從數(shù)據(jù)庫(kù)中提取所有投票結(jié)果并加以顯示。現(xiàn)在假設(shè)投票人列表已經(jīng)提取并保存到變量voterList中,我們可以使用foreach來(lái)請(qǐng)求列表中每個(gè)人的投票結(jié)果,如圖22-11所示。
圖 22-11 使用foreach塊請(qǐng)求列表中每位成員的投票結(jié)果
這里對(duì)變量currentVotesList進(jìn)行初始化,來(lái)清空列表,目的是為了將最新從數(shù)據(jù)庫(kù)中獲得的投票結(jié)果添加到列表中。在foreach中使用TinyWebDB.GetValue來(lái)處理列表中的每一個(gè)Email地址:以Email地址(voterEmail)為標(biāo)簽向數(shù)據(jù)庫(kù)發(fā)送請(qǐng)求。需要注意的是,要等到一系列的請(qǐng)求數(shù)據(jù)返回時(shí)觸發(fā)GotValue事件,才能將投票結(jié)果添加到currentVotesList中。
我們希望在應(yīng)用中顯示投票結(jié)果,事情變得更加復(fù)雜了。在點(diǎn)擊ViewVotesButton按鈕發(fā)出請(qǐng)求之后,在TinyWebDB.GotValue中將收到以每個(gè)Email地址為標(biāo)簽(tag)的數(shù)據(jù),就像“voterlist”標(biāo)簽用于提取用戶Email地址列表一樣。當(dāng)應(yīng)用同時(shí)向數(shù)據(jù)庫(kù)為不同標(biāo)簽請(qǐng)求多余一項(xiàng)的數(shù)據(jù)時(shí),就需要在TinyWebDB.GotValue中編寫代碼來(lái)處理所有可能的請(qǐng)求。(你可能想到編寫多個(gè)GotValue事件處理程序,來(lái)分別處理每個(gè)請(qǐng)求——知道為什么這樣做行不通嗎?)
為了處理這種復(fù)雜的情況,GotValue事件處理程序可以利用自帶的參數(shù)tagFromWebDB,它會(huì)告訴你當(dāng)前的返回值來(lái)自于哪一個(gè)請(qǐng)求。因此,如果標(biāo)簽是“voterlist”,我們可以像之前那樣進(jìn)行處理;如果不是“voterlist”,我們可以假設(shè)它是用戶列表中某人的Email地址,來(lái)源于ViewVotesButton.Click事件處理程序中發(fā)出的請(qǐng)求。當(dāng)這些請(qǐng)求返回時(shí),我們希望將返回的數(shù)據(jù)——投票人及投票結(jié)果——添加到列表currentVotesList中,以便于向用戶顯示。
圖22-12中顯示了整個(gè)TinyWebDB1.GotValue事件處理程序。
更多建議: