
假設你正在一家你很喜歡的書店里翻書,你想了解某一本書在Amazon.com(網上書店)的售價,那么這款“亞馬遜掌上書店”應用就可以幫你實現這一愿望。通過掃描書上的條碼,或輸入書上的ISBN,應用將告訴你這本書當前在Amazon.com的最低售價。你也可以按照主題進行書籍的搜索。
“亞馬遜掌上書店”演示了如何使用App Inventor來創建與web service進行交互的應用(web service又稱作API 或應用程序接口)。本應用將從一個web services上獲取數據,該web services是由本書的作者之一創建的。由本章結束時,你將為自己創建一款定制的應用,來訪問亞馬遜網上書店。
該應用界面非常簡單,用戶可以輸入關鍵字或書的ISBN 碼,應用將列出書名、ISBN以及新書在亞馬遜上的最低售價。也可以使用條碼掃描組件,讓戶可不必輸入文本,而是通過掃描來進行搜索(從技術上講,是掃描儀替你輸入了書的ISBN!)。
## **學習要點**
本章將學習以下內容:
* 在應用中使用條形碼掃描儀功能;
* 用TinyWebDB組件訪問Web信息源(Amazon API);
* 學會處理從web信息源返回的復雜數據。所謂復雜數據,具體來說,就是圖書列表中的每本書都是一個列表,其中包含三個列表項:書名、價格及ISBN。
此外我們將介紹用Python語言及谷歌的App引擎編寫的源代碼,并用它來創建自己的web service API。
## **什么是API?**
在開始設計組件和編寫應用之前,我們來解釋一下什么是API(應用程序接口),以及它如何工作。可以把API想象為一個網站,只是它不與人類交互,而是與其他計算機程序交互。

**圖 13-1 模擬器中的“亞馬遜掌上書店”**
API通常被稱作“服務器端”程序,因為它們的特點就是為“客戶端”程序提供信息。“客戶端”程序負責實現與人類的接口——如App Inventor應用。如果你曾經在手機上使用Facebook 應用,你實際上是通過Facebook的客戶端程序與Facebook的服務器端程序(API)進行通信。
本章將創建一個Android客戶端應用,與Amazon API進行通信。應用將向Amazon API請求書的信息(書名、書號及價格等),而API將向應用返回最新的信息列表,用戶將在應用中瀏覽到書的相關信息。
我們即將使用的Amazon API是App Inventor專用的API。這里我們不想過多地解釋細節,但有關配置的知識是非常有用的,正是由于有了這些配置,我們才能用TinyWebDB組件與Amazon進行溝通。好在你已經學會了使用TinyWebDB!調用TinyWebDB.GetValue來請求信息,然后在TinyWebDB.GotValue事件處理程序中處理返回的信息,就像在用Web數據庫一樣。(如果忘記了,去復習一下第10章的“出題”應用。)
在創建應用之前,需要先了解一下Amazon API協議,協議規定了請求數據的方式以及返回數據的格式。就像不同的族群有不同的禮儀(兩人相遇時,是握手、鞠躬還是點頭?),計算機之間的互相則需要有協議 。
Amazon API為調用者提供了一個Web頁面,來說明API的使用方法。雖然設計API的目的是為了與其它計算機交互,但在這個頁面上,你可以看到這種交互的過程。 按照下列步驟,你可以嘗試調用一個指定tag參數的GetValue,并在頁面上看到返回的數據,這與你在App Inventor中使用TinyWebDB組件請求數據的結果完全一致:
1\. 在瀏覽器中訪問網站[http://aiamazonapi.appspot.com/](http://aiamazonapi.appspot.com/),你會看到如圖13-2所示的頁面(頁面中的中文為譯者添加)。

**圖 13-2 App Inventor專用的Amazon API的說明及測試頁面**
2\. 本頁面允許你對與此API的GetValue功能進行測試:在tag輸入框中輸入搜索詞(如“natural computing”),然后單擊“Get value”按鈕。頁面將顯示從Amazon API返回的排在前五位的書籍列表,如圖13-3所示。

**圖 13-3 調用Amazon API來搜索與tag(或關鍵字)“natural computing”有關的書籍**
返回值是一個書的列表,每本書的信息由一對方括號包圍[像這樣],提供了書名、售價及ISBN。如果仔細觀察,你會發現每本書其實是另一主列表的子列表。主列表(natural computing)由外層的方括號包圍,每個子列表(或書)被封閉在單獨的一對方括號內。所以此API的返回值實際上是一個列表的列表,每個子列表提供一本書的信息。我們來細致地觀察一下。
數據中的每個左括號([)標志列表的開頭。第一左括號標志外層列表(書籍列表)的開始,緊挨著它的左括號是第一個子列表,即第一本書的開頭:
> [['"Natural Computing: DNA, Quantum Bits, and the Future of Smart Machines"', '$5.77', '0393336832'],
子列表包含三個部分:書名、該書在亞馬遜書店的最低售價及這本書的ISBN。當你的App Inventor應用取得這些信息時,就可以使用select list item塊來訪問其中的每個部分,用索引值1訪問書名,索引值2訪問價格,索引值3訪問ISBN。(如果淡忘了有關列表及索引的使用方法,請復習第十章的“出題”應用。)
3\. 除了搜索關鍵字,你還可以通過ISBN來精確地搜索一本書,只要在tag后面直接輸入書號即可,試試輸入“1449397484”,如圖13-4所示。返回結果如下:
> [['"App Inventor: Create Your Own Android Apps"', '$21.64', '1449397484']]
返回結果中的雙括號([[)表示返回的仍然是一個列表的列表,雖然列表中只有一本書。這似乎有點奇怪,但這一點對需要訪問這類信息的應用來說非常重要。

**圖 13-4 用ISBN替代關鍵字在AmazonAPI中查詢書籍**
## **設計組件**
“掌上書店”應用的用戶界面比較簡單:一個用于輸入搜索詞或ISBN的文本框,一個用于啟動搜索(關鍵字或ISBN)的按鈕,另一個啟動掃描書的條碼的按鈕(稍后會用到),一個搜索結果標題的label,另一個顯示搜索結果的label,還有兩個非可視組件:TinyWebDB和條碼掃描儀。表13-1列出了圖13-5中所示的所有組件,對照檢查你的結果。

**圖 13-5 組件設計器中“亞馬遜掌上書店”的用戶界面**
**表13-1 “亞馬遜掌上書店”應用的組件列表**
| 組件類型 | 面板中分組 | 命名| 作用 |
| --- | --- | --- | --- |
| Textbox | User Interface | SearchTextBox |用戶在此輸入關鍵字或ISBN |
| HorizontalArrangement| Layout| HorizontalArrangement1 |將按鈕放置在同一行內|
| Button | User Interface | SearchButton |點擊啟動關鍵字或ISBN搜索 |
| Button | User Interface | ScanButton |點擊開始掃描書的ISBN條碼 |
| Label | User Interface | Label1 | 搜索結果的標題|
| Label | User Interface | ResultLabel | 顯示搜索結果 |
| TinyWebDB | Storage | TinyWebDB1 | 與Amazon.com交互 |
| BarcodeScanner | Sensors | BarcodeScanner1 | 掃描條碼 |
組件的屬性設置如下:
1\. 設置SearchTextBox的Hint屬性為“輸入關鍵字或ISBN”;
2\. 設置Button及Label的Text屬性:如圖13-5所示;
3\. 設置TinyWebDB組件的serviceURL屬性為[http://aiamazonapi.appspot.com/](http://aiamazonapi.appspot.com/)?。
## **設計行為**
在塊編輯器中設定下列行為:
* 按關鍵字搜索:用戶輸入關鍵字并點擊SearchButton來調用Amazon搜索。通過調用TinyWebDB.GetValue來實現這一點;
* 按ISBN搜索:用戶輸入一個ISBN并點擊SearchButton;
* 條碼掃描:用戶點擊ScanButton啟動掃描儀,掃描ISBN完成后將啟動Amazon搜索;
* 處理圖書列表:首先采用默認方式顯示Amazon返回的數據。稍后再加以修改,采用有組織的方式來顯示每本書的書名、售價及ISBN。
### **按關鍵字或ISBN搜索**
用戶點擊SearchButton時,從SearchTextbox中獲取搜索內容,并以此為tag,使用TinyWebDB.GetValue塊向Amazon API請求搜索數據。
當Amazon返回結果時,將觸發TinyWebDB.GotValue事件。現在用ResultsLabel直接顯示返回結果,如圖13-6所示,當見到這些真實的數據后,可以采取更為復雜的方式來顯示數據。

**圖 13-6 向API發送搜索請求,并將結果顯示在ResultsLabel中**
#### **塊的作用**
當點擊SearchButton時,TinyWebDB1.GetValue開始向API發出請求。隨請求一同發送的tag正是用戶在 SearchTextBox中輸入的內容。
從第十章“出題”應用中得知,TinyWebDB.GetValue發出的請求并不能立即獲得結果,而是在收到從API返回的數據時,觸發TinyWebDB1.GotValue事件。在GotValue塊中,首先檢查返回值是否為列表,如果是,則數據被寫入ResultsLabel。(如果Amazon API離線或搜索的關鍵字不存在,將沒有數據返回。)再輸入ISBN“1118717376”試試看。

**圖 13-7 搜索“Android”返回的結果**
>  測試:在輸入框中輸入搜索詞,如“Android”,然后點擊“搜索”按鈕。你將得到類似于圖13-7的結果。注意:手機上返回的結果與網頁上的結果略有不同:方括號變成了圓括號。(這個界面看起來很糟糕,稍后我們會處理。)
### **消除用戶的困惑**
在前幾章中了解到,使用TinyWebDB請求數據的過程需要一點時間,在收到返回的數據之前,用戶界面上悄無聲息,這時用戶會感到困惑,因此從用戶友好的角度考慮,添加一點提示是非常必要的。這里我們用ResultLabel來顯示提示信息。如圖13-8所示。但如果網絡速度很快,數據很快返回并觸發GotValue事件,則幾乎看不到提示,數據就顯示出來了。

**圖 13-8 添加提示信息消除用戶的困惑**
### **掃描一本書**
現實情況是:在手機上輸入字符通常不那么容易,而且總會出一些小錯。如果能夠在應用中使用條碼掃描,那么問題會變得簡單(并且幾乎不會出錯)。這是Android手機內置的另一項強大的功能,你可以用App Inventor輕而易舉地實現它。
函數BarcodeScanner.DoScan用于啟動掃描儀,可以在ScanButton被點擊時調用它。一旦掃描操作完成,將觸發BarcodeScanner.AfterScan事件。該事件帶有一個參數result,其中保存了掃描所獲得的信息。在本例中,result即是用于搜索的ISBN,如圖13-9所示。

**圖 13-9 用戶掃描書上的條碼獲得ISBN并啟動搜索**
#### **塊的作用**
點擊ScanButton將啟動掃描儀,即執行BarcodeScanner1.DoScan。掃描完成時觸發AfterScan事件。該事件帶有result參數,在本例中為書的ISBN。用ResultLabel告訴用戶正在進行搜索,并在SearchTextBox中顯示result(掃描獲得的ISBN),最后調用TinyWebDB.GetValue來啟動搜索。仍然使用之前定義的TinyWebDB.GotValue事件處理程序來處理返回的書籍信息。
>  測試:點擊ScanButton并掃描一本書的條碼。應用中是否顯示了該圖書的信息?
### **改進信息的顯示**
我們所創建的這類客戶端應用,可以按需要來處理收到的數據,可以與其他網上商店進行價格比較,也可以用書名信息來搜索其他圖書館中的同類書籍。
通常是將加載自API的信息保存到變量中再做處理,而目前只是在TinyWebDB.GotValue事件處理程序中,將Amazon API返回的所有信息直接寫入到ResultsLabel中。
下面我們來處理(或這說安排)這些數據,方法是(1)將返回數據中的每本書的書名、售價以及ISBN分別保存到單獨的變量中;(2)以一種有序的方式顯示這些項目。
到目前為止,你已熟知了全局變量(global variable),與之對應的是局部變量(local variable),這是本章引入的一個新的概念。一個變量有它的有效范圍:全局變量直接在塊編輯器中定義,單獨占一行,不屬于任何一組塊,而所有的塊都可以調用全局變量;局部變量則定義在某個程序塊內部,它將包含其它塊,而且只對其包含的塊有效,其余的所有塊都無法訪問到它。
下面將使用這些變量,并將它們顯示出來,試試看按需要創建這些變量,并組織一些塊來分行顯示每一項搜索結果,完成之后與圖13-10進行比較。
#### **塊的作用**
這里定義了四個變量:resultList、title、cost及ISBN,用來保存Amazon API返回數據中的每一條數據。從API返回的數據保存在參數valueFromWebDB中,這里將它另存為resultList。其實程序可以直接使用valueFromWebDB,但通常會將它另存為一個變量,以便在該事件處理程序之外也可以使用這一數據。(像valueFromWebDB這樣的參數僅在事件處理程序內有效,事件處理程序之外無法訪問該參數值。)
foreach循環用來遍歷返回結果中的每個數據項。回想一下,從Amazon返回的數據是一個列表的列表,每個子列表代表一本書的信息,因此foreach塊中的項變量被命名為bookItem,它保存了當前正在被遍歷的書的信息。
現在我們要清醒地面對項變量bookitem,它是一個列表,其中第一項是書名,第二項是售價,第三項是ISBN,因此,select list item塊利用索引值(index)將這些數據逐項提取出來,并保存在事先定義的變量中(title、cost及ISBN)。

**圖 13-10 在遍歷過程中提取每本書的書名、售價及ISBN,并逐行顯示它們**
一旦數據被拆解成這種方式,就可以隨意地擺布它們。程序中的局部變量只是作為join塊的組成部分,來逐行顯示書名、售價及ISBN。
>  測試:嘗試搜索其他書,看看返回的信息是如何顯示的。應該類似于圖13-11。

**圖 13-11 用更有條理的方式顯示搜索結果**
### **定制化API**
我們所連接的API([http://aiamazonapi.appspot.com](http://aiamazonapi.appspot.com/))是由Python(編程語言)和谷歌應用引擎(App Engine)創建的。開發者可以將應用引擎上創建并發布網站或服務(API)。只有當你的網站或服務非常受歡迎時(這意味著你使用了大量的谷歌服務),才需要向App Engine付費。
這里使用的API只能訪問全部Amazon API中的有限的部分:最多只能查詢到五本書。如果想提供更多靈活的訪問,例如,不僅是找書。你可以從[http://appinventorapi.com/amazon/](http://appinventorapi.com/amazon/)下載源代碼,并按照自己的需要來修改它。
這種修改確實需要有Python編程的知識,所以要小心!但是加入你已經通過本書完成了App Inventor的學習,那么是該考慮迎接新的挑戰了。想要學習Python,可以查看網上的文章《如何像計算機科學一樣思考:學會使用Python》([http://openbookproject.net//thinkCSpy/](http://openbookproject.net//thinkCSpy/)),并查看本書第24章的“創建App Inventor API”部分。
## **改進**
一旦應用運行起來,你可能會探索做一些改進。如:
* 如果用戶的搜索沒有任何返回值(比如當用戶輸入了一個無效的ISBN),程序沒有任何反饋,修改或添加塊,當沒有返回值時通知用戶;
* 修改程序,只查找低于10美元的書籍;
* 修改程序,當掃描一本書后,用聲音來報告Amazon的最低售價(使用第七章“Android,我的車在哪兒”中用過的TextToSpeech組件);
* 從[http://examples.oreilly.com/0636920016632/](http://examples.oreilly.com/0636920016632/)下載[http://aiamazonapi.appspot.com](http://aiamazonapi.appspot.com/)API,修改它,使之能返回更詳細的信息。例如,返回每本書在Amazon上的網址(URL),并隨每本書的信息一同顯示;用戶點擊URL可以打開相應的頁面。正如前面提到的,修改API需要Python編程和谷歌App Engine的一些知識。更多信息請參見第24章。
## **小結**
以下是本章涵蓋的內容:
* 使用TinyWebDB組件以及指定的API,在應用中訪問互聯網API。將TinyWebDB組件的serviceURL屬性設置為API的地址(URL),并調用TinyWebDB.GetValue來發出請求。請求的數據不會立即返回,可以在TinyWebDB.GotValue事件處理程序中訪問到該數據;
* BarcodeScanner.DoScan可以啟動掃描儀。當用戶完成條碼掃描時,將觸發BarcodeScanner.AfterScan事件,并將掃描獲得的數據保存在參數result中;
* 在App Inventor中,復雜數據可以表示為列表以及列表的列表。如果知道從API返回的數據的格式,就可以使用foreach及select list item來分別提取沒條信息,并將其保存到變量,這樣就能以你需要的方式,對變量進行處理或顯示。
- 簡介
- 序言
- 前言
- 第 1 章 Hello 貓咪
- 第 2 章 油漆桶
- 第 3 章 打地鼠
- 第 4 章 開車不發短信
- 第 5 章 瓢蟲快跑
- 第 6 章 巴黎地圖旅游
- 第 7 章 安卓,我的車在哪?
- 第 8 章 總統測驗
- 第 9 章 木琴
- 第 10 章 出題及答題
- 第 11 章 廣播中心
- 第 12 章 遙控機器人
- 第 13 章 亞馬遜掌上書店
- 第 14 章 理解應用的結構
- 第 15 章 軟件工程與應用調試
- 第 16 章 應用中的存儲
- 第 17 章 創建動畫應用
- 第 18 章 程序中的決策:條件塊
- 第 19 章 數據列表編程
- 第 20 章 循環
- 第 21 章 定義過程
- 第 22 章 數據庫
- 第 23 章 傳感器
- 第 24 章 與Web API通信