[譯]不要在UI主線程中進行耗時的操做

原文: Why Ice Cream Sandwich Crashes your Apphtml

問題

自Android Ice Cream Sandwich發佈後, 這個問題就開始在StackOverflow彌散開來:java

個人應用在Android2.x上運行良好,可是在3.x 和4.x系統上老是強退,是什麼致使的?android

這是一個很棒的問題,畢竟開發者老是但願基於舊版本系統開發的應用在新版本的Android系統仍能兼容。在我看來,問題的緣由可能多種多樣。 但大多數時候,緣由很是簡單:你把一個可能很是耗時的操做放進了UI線程。數據庫

什麼是UI線程?

應用的主UI線程的概念及其重要性是每一個Android開發者都應理解。當一個應用啓動,系統會爲應用建立一個名爲「main」的主線程。這個主線程(也就是UI主線程)主要負責把事件分發給合適的view或者widget, 所以它很是重要。它也是你的應用和應用的UI交互的線程。例如,若是你點擊了屏幕上的一個按鈕,UI線程會把點擊時間交給view處理,view接到事件後會設置它的pressed狀態,而後向事件隊列中發送一個invalidate請求。 UI線程會依次讀取隊列而且告訴view去重繪本身。網絡

除非你的Android應用實現的很是合理,不然這個單線程模型會使性能變得極低。在極端狀況下,若是UI線程負責整個應用中的全部操做,進行耗時的操做好比發送網絡請求,或者數據庫查詢等都會致使用戶界面的阻塞。這些操做在未完成以前,全部的時間包括繪製和觸屏事件都不會被派發。從用戶的角度來看,程序彷佛是卡死了。app

在這些狀況下,即時的反饋至關重要。研究代表0.1s是用戶感受系統是否流暢的臨界值。任何比臨界值更慢的都被認爲延遲(Miller 1968; Card et al. 1991)。雖然1秒看起來沒什麼影響,但在GooglePlay中,即使是十分之一秒也多是好評和差評的區別。更糟糕的是,若是UI線程被阻塞5秒以上,用戶會收到「程序未響應」(ANR)的提示對話框,而且會強制退出。異步

爲何Android會使應用崩潰

應用在2.x系統運行良好,在3.0及以上平臺上崩潰的主要緣由在於,3.0以上平臺在處理UI線程資源濫用上更加嚴格。好比說,3.0平臺檢測到UI線程中有網絡請求時,會拋出NetworkOnMainThreadExceptionwill的異常:性能

E/AndroidRuntime(673): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example/com.example.ExampleActivity}: android.os.NetworkOnMainThreadException網站

Android developer網站文檔中也對此進行了很好的解釋:線程

當應用試圖在主線程中進行網絡操做,NetworkOnMainThreadException會被拋出。只有在運行Honeycomb SDK及更高的版本中會被拋出。更早版本的SDK容許在主事件循環線程中進行網絡操做,可是很是很是不鼓勵這麼作。

列出一些ICS和Honeycomb不容許在UI線程中進行的操做:

  • 打開套接字鏈接 (i.e. new Socket()).
  • HTTP 請求 (i.e. HTTPClient and HTTPUrlConnection).
  • 試圖鏈接遠程的 MYSQL 數據庫.
  • 下載文件 (i.e.Downloader.downloadFile()).

若是你要在UI線程中進行某些操做,必定要把它們打包到一個工做線程中。其中最簡單的方式是使用AsyncTask, 它容許你在你的用戶界面中進行一些異步的操做。AsyncTask會把阻塞操做放到工做線程中,並把結果返回到UI線程,而你不須要處理任何與線程相關的工做。

結論

我決定寫這篇主題的念頭來源於我在StackOerflow和其它論壇上無數次看到了這個問題。問題的主要來源是在UI線程進行了耗時的操做。爲了確保用戶界面保持流暢,有必要把執行套接字鏈接、HTTP請求、文件下載和其餘的耗時操做放到一個單獨的線程中。最簡單的方法就是把操做打包到AsyncTask中,它會幫助你啓動新的線程並讓他們與你的用戶界面異步交互。

有幫助的連接

這些資料可能會幫助你熟悉AsyncTask

AsyncTask documentation
Multithreading For Performance

相關文章
相關標籤/搜索