在一次調試問題中,出現下面錯誤信息:html
09-07 09:15:08.000 1342-1342/? W/System.err﹕ android.os.NetworkOnMainThreadException 09-07 09:15:08.000 1342-1342/? W/System.err﹕ at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1133) 09-07 09:15:08.000 1342-1342/? W/System.err﹕ at java.net.InetAddress.lookupHostByName(InetAddress.java:385) 09-07 09:15:08.000 1342-1342/? W/System.err﹕ at java.net.InetAddress.getAllByNameImpl(InetAddress.java:236) 09-07 09:15:08.000 1342-1342/? W/System.err﹕ at java.net.InetAddress.getByName(InetAddress.java:289) 略。。。。。。
後來百度/Google"android.os.NetworkOnMainThreadException", [1]中在2013-06-24的博文中就指出:NetworkOnMainThreadException是由於應用程序在主線程上嘗試網絡操做。另外,這個異常在Honeycomb SDK或更高版本的SDK上纔會拋出。低版本的SDK容許應用程序在主線程或loop線程執行網絡操做,但不鼓勵這樣作。應該放到子線程中,而後當子線程完成以後再發廣播也好,message也好,通知主線程作相應的UI更改,或利用AsyncTask來作網絡操做。java
這個問題涉及到AndroidManifest.xml文件中的SDK版本配置,並且隨着Android Studio缺省使用Gradle來編譯,build.gradle文件中也存在相應的SDK版本配置,那麼如何來正確地配置這些SDK版本呢?android
參考[2], AndroidManifest.xml文件中,<uses-sdk>標籤中有minSdkVersion, targetSdkVersion, maxSdkVersion三個屬性,因爲maxSdkVersion屬性已經再也不推薦配置,所以下面主要討論另外兩個屬性。shell
首先minSdkVersion與targetSdkVersion都是整數值,一般都須要配置而不要採用默認值。android-studio
minSdkVersion是應用程序運行所需的SDK最低版本。若是不指明該屬性,其默認值爲1。若是Android手機版本低於這個值,應用程序將安裝失敗。網絡
[3]中經過查看Android源碼和兩個例子來理解targetSdkVersion:
app
例子1: DatePickerDialog組件oop
當AndroidManifest.xml文件中 targetSDKversion屬性設置不一樣的值,DatePickerDialog組件表現會不一樣。若是設置的值爲10或更低的值,DatePickerDialog爲左側顯示。若是設置的值爲11或更高的值,DatePickerDialog爲右側顯示。學習
Android源碼以下(注意:下面代碼可能在最新的SDK版本中有變化,但判斷targetSdkVersion的邏輯沒變):測試
public DatePickerDialog(Context context, OnDateSetListener callBack, int year, int monthOfYear, int dayOfMonth, boolean yearOptional) { this(context, context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB ? com.android.internal.R.style.Theme_Holo_Light_Dialog_Alert : com.android.internal.R.style.Theme_Dialog_Alert, callBack, year, monthOfYear, dayOfMonth, yearOptional); }
在第7行根據設置的targetSDKversion來設置不一樣的theme:getApplicationInfo().targetSdkVersion >= SOME_VERSION。
實際上在Android源碼中處處都有相似的判斷
。
例子2: Webview類
當設置 targetSDKversion屬性爲18或更高值時,Webview類的public方法應當在主線程中調用,不然運行時系統將拋出一個RuntimeException
。可參見下面源代碼:
sEnforceThreadChecking = context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN_MR2; if (sEnforceThreadChecking) { throw new RuntimeException(throwable); }
Android官方文檔中提到, 隨着Android新版本的發展,一些行爲甚至appearances 可能發生改變。從前面兩個例子已經能夠看出。
總結一下:當設置targetSdkVersion爲更高的值時,就像DatePickerDialog例子中那樣會有appearance加強的好處,但正如Webview例子那樣,之前在低版本中正常運行的代碼,若是修改targetSdkVersion爲更高值時可能會引起致命錯誤,但此時在開發階段就能夠修訂代碼來適應更高的targetSdkVersion。所以targetSdkVersion屬性告訴系統該應用已經在target Version系統上進行了充分測試。
但可能targetSdkVersion這個SDK版本中的某個新特性在低版本中是不支持的,那麼在低版本的API設備上運行程序時,可能會報錯:java.lang.VerifyError。也就是說,此屬性不會幫你解決兼容性的測試問題。你至少須要在minSdkVersion這個版本上將程序完整的跑一遍來肯定兼容性是沒有問題的。
所以,須要在minSdkVersion與targetSdkVersion分別須要將程序完整的跑一遍來確保兼容性沒有問題。
build.gradle中會有如下代碼:
android { compileSdkVersion 19 buildToolsVersion "21.1.1" defaultConfig { minSdkVersion 8 targetSdkVersion 19 }
AndroidManifest.xml
文件中有如下代碼:
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="8"/>
Gradle文件會覆蓋manifest文件中的值,能夠不用管mainfest文件中的值,將minSdkVersion和targetSdkVersion都在Gradle文件中配置便可,也能夠刪除mainfest文件中的<uses-sdk>標籤。
在build.gradle文件中,
compileSdkVersion是編譯時Android的API level。
buildToolsVersion是編譯器(aapt, dx, renderscript compiler等)的版本. 從版本18開始的每一個API level, 會有一個匹配的.0.0編譯器版本。例如在IO 2014, 發佈了API 20和build-tools 20.0.0。
minSdkVersion與targetSdkVersion在前面已經討論過。
注意:targetSdkVersion不該當超過compileSdkVersion。
在[4]中還給出了一種統一各個版本的方法:
編輯 gradle.properties
文件,增長如下代碼:
ANDROID_BUILD_MIN_SDK_VERSION=14 ANDROID_BUILD_TARGET_SDK_VERSION=21 ANDROID_BUILD_TOOLS_VERSION=21.1.3 ANDROID_BUILD_SDK_VERSION=21
而後,就能夠在 build.gradle
文件中這樣使用:
//...android { compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION) buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION defaultConfig { minSdkVersion Integer.parseInt(project.ANDROID_BUILD_MIN_SDK_VERSION) targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION) } }//..
在build.gradle文件中,除了上述與版本相關的配置之外,在dependencies中也涉及編譯期間的版本配置,這留待後續再學習研究:
dependencies { compile 'com.android.support:support-v4:20.0.0' // your unit test dependencies as described in the article unitTestCompile files("$project.buildDir/classes/release") unitTestCompile 'junit:junit:4.11' unitTestCompile 'com.google.android:android:4.1.1.4' unitTestCompile 'org.robolectric:robolectric:2.1.1' // duplicate these dependencies in the instrumentTestCompile scope // in order to have the integration in Android Studio (autocompletion and stuff) instrumentTestCompile 'junit:junit:4.11' instrumentTestCompile 'org.robolectric:robolectric:2.1.1' }
5. 參考資料
[1] android.os.NetworkOnMainThreadException的解決方案, 2013-06-24, http://www.cnblogs.com/kissazi2/archive/2013/06/24/3153307.html
[2] Android中<uses-sdk>屬性和target屬性分析, 2014-06-03, http://blog.csdn.net/fuzhengchao/article/details/28121193
[3] Android Min SDK Version vs. Target SDK Version, http://stackoverflow.com/questions/4568267/android-min-sdk-version-vs-target-sdk-version
[4] How do I add a library project to the Android Studio?http://stackoverflow.com/questions/16588064/how-do-i-add-a-library-project-to-the-android-studio/16639227#16639227