相信在很多使用最新版本Android系統開發網絡程序的同學在入門的時候都會遇到後臺拋出異常的問題。仔細查閱代碼之後,發現和書上或者網絡上的代碼是一致的,而且也已經在AndroidManifest.xml中經過授權。但是,後臺仍然拋出瞭如下異常:
LogCat提示,系統拋出瞭android.os.NetworkOnMainThreadException異常。
經過反復實驗,發現,當我們用數據流方式獲取網絡資源(比如一個頁面的源文件)的時候,系統會拋出此異常,比如,下面這兩條語句:
InputStream inStream =conn.getInputStream();
inStream.read(buffer)
此時,該異常被系統拋出。
通過查閱相關資料,發現,自從Android 2.3之後,系統增加瞭一個類:StrictMode。這個類對網絡的訪問方式進行瞭一定的改變。
Android的官方文檔給出瞭這個類設置的目的:
StrictMode是一個系統提供的開發工具,用以檢測在開發過程中因為偶然的事故從而造成的系統潛在的問題,進而提示開發者對其進行修復。
StrictMode通常用於捕獲磁盤訪問或者網絡訪問中與主進程之間交互產生的問題,因為在主進程中,UI操作和一些動作的執行是最經常用到的,它們之間會產生一定的沖突問題。將磁盤訪問和網絡訪問從主線程中剝離可以使磁盤或者網絡的訪問更加流暢,提升響應度和用戶體驗。
顯然,大多數初學者在進行網絡開發時,會選擇將訪問網絡的代碼直接放到主進程中,由於和主進程的首要工作——UI交互——相矛盾,因此,必須設置一定的檢測機制,以保證系統運行的流暢,所有的異常都可以被檢測。
Android文檔建議我們增加這兩條命令:
StrictMode.setThreadPolicy(newStrictMode.ThreadPolicy.Builder().detectDiskReads().detectDiskWrites().detectNetwork().penaltyLog().build());
StrictMode.setVmPolicy(newStrictMode.VmPolicy.Builder().detectLeakedSqlLiteObjects().detectLeakedClosableObjects().penaltyLog().penaltyDeath().build());
現在讓我們看看它們都是做什麼的。
public static voidsetThreadPolicy(StrictMode.ThreadPolicy policy)
這個方法允許我們為當前應用設置一組線程運行策略機制。其中的參數是一個策略組(即一組策略)。
public static finalclass StrictMode.ThreadPolicy.Builder()
Builder是StrictMode中內嵌類ThreadPolicy的一個內嵌類,在此我們調用瞭它的默認構造方法。
detectDiskReads().detectDiskWrites().detectNetwork().penaltyLog().build()
通過這種方式,我們設置瞭一組監控模式,我們要檢測磁盤的讀寫,網絡的訪問, Log中的違規等。
第二條語句設置瞭虛擬機的一組監控策略,參數一致,因此不再贅述。
這樣,在保證瞭網絡和磁盤訪問受控之後,主線程就允許我們對網絡資源進行訪問。
最後,需要說的是,策略限制隻需要在主線程運行開始階段,也就是onCreate剛被調用的時候使用添加,其後的所有方法都將遵循這一規則。
摘自 北京大學-Google Android實驗室