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