如何簡化安卓網絡調用:介紹 volley 庫
生存在一個由互聯網驅動的世界之中,移動應用需要從它們的產品后端(例如,從數據庫)還有像 非死book 和 推ter 這樣的第三方資源那里共享和接收信息。這些交互經常是通過 RESTful API 來進行了。當請求的數量增長的時候,請求發起的方式對于開發部門而言會變得越來越值得重視, 因為你獲取數據的方法會實實在在的影響到一個應用的用戶體驗。
本文我想要從API層面來分享一下我在Android開發中使用網絡庫的一些經驗。我會先從基礎的同步和異步編程開始講起,并且會涵蓋復雜的 Android 線程問題。然后我們會深入到 AsyncTask 模塊當中去, 理解其架構流程并且閱讀示例代碼以了解其具體實現。我也會聊到 AsyncTask 庫的局限性,并向各位介紹 Android Volley ,它可以作為發起異步網絡調用的一種更好的方法。之后我們會深入去了解一下 Volley 的架構并用代碼示例來向大家介紹一下其中那些有價值的功能特性。
說到這兒你還能提起勁頭嗎? 把 Android 網絡編程這塊搞定可是能帶你成為一名應用開發老手滴哦。
注意 : 其它一些具備網絡開發能力的 Android 庫不在本文涵蓋的內容之列, 包括 Retrofit , OkHttp 。建議你自行去對他們進行一下了解。
編程方法簡言之就是: 同步和異步
“等下,老媽,我來了。” Jason 這樣說著, 仍然坐在沙發上,等著他的女朋友回消息,一小時前他聯系過她的。“你應該在等女朋友回消息的時候打掃一下你的房間。” Jason 的媽媽回應他道,語帶諷刺。她的建議不是很明顯嗎? 這樣的場景跟同步和異步 HTTP 請求的情況很像。讓我們來瞧瞧。
同步請求的行為像 Jason,保持等待,直到有響應從服務器發過來。同步請求會阻塞界面,增加計算的時間,并且使得應用失去響應(也不總是這樣 — 有時候停止等待沒有意義,比如銀行交易)。更聰明的一個辦法正如 Jason 的老媽所建議的。在異步的世界中,當客戶端向服務端發起了一次請求, 服務端會將該請求分派給事件處理器,注冊一個回調然后就轉向下一個請求。當有了響應以后,客戶端就會響應該結果。這是一種更棒的方法,因為異步請求可以各個任務獨立執行。
上圖展示了兩種編程方法之間在客戶端-服務器模型中的區別。在 Android 中, UI 線程也常被稱作主線程, 基于的是跟同步編程一樣的理念。
Android 線程
線程是由操作系統所管理的指令的集合。多個線程會運行在一個進程之下 (類似于 Android 中的 Linux 進程) 并且共享諸如內存這樣的資源。在 Android 中, 當應用運行起來的時候,系統會為整個應用程序創建一個執行線程, 被稱作 “主” 線程 (或者說 UI 線程)。主線程工作在一個單線程模型之中。它負責處理 UI 組件(繪制事件)事件的派發工作, 以及通過 UI 工具集與組件進行交互,比如 View.OnClickListener(), 并且對系統事件做出相應,比如 onKeyLongPress()。
UI 線程運行在一個無線循環之中,并且對消息隊列進行監視以檢測是否需要更新 UI。讓我們來看一個示例。但用戶觸摸了一個按鈕,UI線程會將觸摸事件派發給某個組件, 該組件是能對其按壓狀態進行設置,并且向消息隊列發送一個請求。UI線程會從消息隊列取出該請求,然后通知組件執行動作 — 在該場景中,組件會重繪自己以表明按鈕被按下去了。如果你對深入了解UI線程感興趣的話,應該去讀一讀關于 Looper , MessageQueue 以及 Handler 類的相關內容, 就是它們完成了我們示例中所談論的任務。如你所預料的,UI線程肩負著許多的職責,比如:
-
啟動一個 activity;
-
執行一個服務;
-
響應系統回調。
當你在思考這個問題的時候,你的單線程UI線程執行著所有的工作來響應用戶的交互。因為所有的事情都發生在UI線程身上,所以諸如數據庫查詢以及網絡調用這樣的耗時操作就會把 UI 阻塞掉。UI線程會將事件派發給 UI 組件。應用的表現不佳,用戶會感覺應用卡死了。如果這些任務花掉的時間讓UI線程被阻塞4至5秒鐘的話,Android 會拋出一個 “ Application Not Responding ” (ANR) 錯誤。這樣一個對用戶不怎么友好的 Android 應用,就別想得到什么好評了,也許隨之而來的還有糟糕的評分和匆匆忙忙的卸載。
利用主線程來處理長耗時任務會把事情搞砸。如果UI線程是非阻塞的,那么你的應用將總是能持續響應用戶事件。這就是為什么如果你的應用程序需要發起網絡調用,而調用需要在運行于后臺的工作線程來執行,而不是主線程。你可以使用一個 Java 的 HTTP 客戶端庫來通過網絡發送和接收數據, 但是網絡調用本身應該由一個工作線程來負責執行。等等,在Android里面還有另外一個問題: 線程安全。
Android UI 工具集是非 線程安全 的。如果工作線程(執行了發起網絡調用的任務)更新了 Android UI 工具集, 就可能導致未定義且非預期的行為發生。這樣的問題追蹤起來困難且費時間。單線程模型可以確保在同一時刻 UI 不會被不同的線程所修改。因此,如果我們得用一張來自于網絡的圖片來更新 ImageView 的話,工作線程會在一個獨立的線程中執行網絡操作,而 ImageView 會由 UI 線程來更新。這樣的話,有了 UI 線程提供必要的同步機制,就確保了操作是線程安全的。這樣也能讓 UI 線程總是保持非阻塞狀態,因為實際的任務都發生在后臺工作者線程中。
總而言之,在 Android 開發中要遵循如下兩條簡單的規則:
-
不要阻塞 UI 線程。
-
UI 工具集不能直接通過一個非 UI 線程的工作線程被更新。
當說到要向一個“activity”發起請求的時候,你會碰到 Android 的“ 服務 ”。服務就是一個應用組件,它可以在后臺執行長耗時操作,不需要應用處理活動狀態,或者甚至是在執行時用戶已經切換到了另外一個應用。例如,服務能讓你很好地在后臺播放音樂或者下載內容。如果你選擇使用一個服務的話,它默認還是在主線程里面運行,因此你就需要在服務中創建一個新的線程來處理阻塞操作。如果你需要在主線程的外部執行操作,而用戶此時正在同應用進行交互的話,最好就是利用像 AsyncTask 或者 Volley 這樣的網絡庫。
在工作線程中執行任務挺好的,不過要是你的應用開始要執行復雜的網絡操作的話,用工作線程維護起來就困難了。
AsyncTask 是個坑
現在已經相當清楚了,我們應該使用一種穩定的 HTTP 客戶端庫,并且要確保網絡任務是使用工作線程在后臺來實現的 — 其實就是要使用非 UI 線程。
Android 有個資源能幫助我們實現異步地處理網絡調用。 AsyncTask 是一個可以讓我們在用戶界面上執行異步操作的模塊。
AsyncTask 會在一個工作線程中執行所有的阻塞操作, 比如網絡調用,并且在完成以后對結果進行發布。UI 線程會獲取到這些結果并據此對用戶界面進行更新。
Android上的異步任務
如下是我使用 AsyncTask 實現一個異步工作線程的步驟:
-
定義一個 AsyncTask 的子類實現 onPreExecute() 方法, 它會創建一條提示消息,提示說網絡調用即將發生。
-
實現 doInBackground(Params...) 方法。顧名思義,doInBackground 就是發起網絡調用并保護主線自由之身的那個工作線程。
-
因為工作線程不能直接更新 UI,所以我實現了自己的 postExecute(Result) 方法, 它會傳遞來自于網絡調用操作的結果并且在 UI 線程中運行,如此用戶界面就可以被安全地修改了。
-
后臺任務的執行進度可以使用 publishProgress() 方法從工作線程那里發布出來,并且使用 onProgressUpdate(Progress...) 方法在UI線程上進行更新。這些方法并沒有在示例代碼中實現,不過用起來相當直接明了。
-
最后,從 UI 線程使用 execute() 方法將異步任務調用起來。
注意: execute() 和 postExecute() 兩者都運行于 UI 線程之上,因此 doInBackground() 是一個非 UI 的工作線程。
來自:https://www.oschina.net/translate/introducing-the-volley-http-library-to-simplify-networking-in-android