Chinese (Traditional) (中文(繁體)) translation by tianma (you can also view the original English article )
測試是安卓開發中至關重要的一部分,測試能夠在你對外發版之前發現所有潛伏在app中的bug,錯誤和性能問題。
每當你遇到問題的時候,安卓會產生一個錯誤資訊,要麼顯示在AS(Android Studio)的 Logcat Monitor 視窗,要麼在你測試app的設備上顯示。
這些錯誤資訊比較典型,它們短而切中要害,一眼望去似乎毫無幫助。然而這些資訊實際上包含了你用來追蹤工程代碼的所有資訊,你只需要知道如何解讀它們。
在這篇文章中,我們將研究13個在安卓開發中最常遇到的錯誤。我們將仔細觀察每一條錯誤資訊,弄清楚含義,可能的原因,還有最重要的,一步一步叫你怎麼解決這些錯誤。
Spotting Error Messages
當測試app時可能會遇到各種各樣的錯誤資訊,從第一次安裝在目標設備時導致app崩潰的嚴重錯誤,到不斷降低app性能的小問題。
根據你遇到的錯誤類型,安卓會將錯誤資訊顯示在AS或者設備上。
顯示在物理設備或者AVD上的錯誤資訊比較簡單,你只需要注意顯示在設備螢幕對話方塊上的錯誤資訊。然而出現在AS上的錯誤資訊比較困難,因為Logcat Monitor記錄了大量的資訊,我們很容易錯過重要的資訊。
想不要錯過任何重要因資訊,最簡單的方法是打開Logcat Monitor的 Verbose 下拉式功能表並選擇 Error ,這樣就可以過濾出錯誤資訊。
1. R.layout.main Cannot Be Found / Cannot Resolve Symbol R
當AS不能正確的生成
R.java
檔時導致該錯誤,該錯誤隨時都可能發生,這一分鐘還運行良好,可能下一分鐘工程的每個部分都編譯報錯。更糟的是,當AS遇到此問題,所有的
R.layout
資源檔都報錯,這使得AS很難知道哪裡才是代碼錯誤開始的地方。
一般來說,最有效的方法是最簡單的: clean 然後rebuild 工程。在AS工具列選擇 Build > Clean Pro ject ,稍等一下,然後選擇Rebuild Project來重新構建工程。
假如一次完整的操作clean/rebuild沒有生效,多試幾次。畢竟有開發者反應多次操作後生效了。
如果你移動了一些檔和目錄後導致了該問題,可能是AS緩存和你工作工程的
R.layout
不匹配。你懷疑是這種情況的話,在AS工具看選擇
File > Invalidate Caches / Restart > Invalidate and Restart
。
資源檔名也有可能導致該問題,所以確保沒有多個資源檔重名,沒有檔案名包含非法字元。AS支援的合法字元包括小寫字母a-z,0-9,句號和底線,只要有一處檔案名含有非法字元,就會導致整個工程出錯,即使該檔沒有在工程的任何地方 使用 。
假如你剛發現並解決了一個錯誤,但是AS仍然顯示
R.layout
錯誤,你可能需要操作一次clean/rebuild來使得剛才的變化生效。
2. Too Many Field References….Max is 65,536
當你編譯app的時候,APK包含可執行的位元組碼檔,該檔以DEX(Dalvik Executable)位元組碼形式存在。根據DEX 規格,單一DEX 檔最多有65535個方法,如果你的app方法數量超過這個數,你就會遇到 Too many fields… 這個錯誤。 要注意的是,這個方法數量限制指的是你工程中 引用 到的方法,而不是 定義 的方法。
如果你遇到這個問題,你可以從以下方法中選擇一個:
打開multidex支援的過程將依賴於你的工程支援的安卓版本。
假如你工程的目標版本為安卓5.0或者更高,第一步是打開module的build.gradle 文件,然後設置
multiDexEnabled
為
true
。
android {
defaultConfig {
minSdkVersion 21
multiDexEnabled true
然而假如你的minSdkVersion為20或者更低,那麼你需要增加屬性multiDexEnabled true,然後增加multidex支援函式庫的依賴。
dependencies {
compile 'com.android.support:multidex:1.0.1'
下一步得看你是否重寫了Application類。
假如你的工程重寫了Application類,那麼你打開Manifest檔,然後增加如下的標籤: <application>
<application
android:name="android.support.multidex.MultiDexApplication" >
</application>
假如你的工程沒有重寫Application,那麼你需要繼承MultiDexApplication:
public class MyApplication extends MultiDexApplication
最後假如你的工程重寫了Application,但是沒有改變基類,那麼你可以通過重寫attachBaseContext()方法,並且調用MultiDex.install(this)來打開multidex,比如,
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
3. Please Choose a Valid JDK Directory
當你在build你的app時遇到這個錯誤,那意味著AS正試圖找到JDK在電腦中的安裝位置。
解決這個錯誤的方法



如果還是沒有解決問題,那麼選擇File > Project structure… > SDK Location, 然後手動輸入JDK的完整路徑。如果你不知道JDK安裝在電腦的哪個地方,你可以打開Mac系統的終端或者Windows的cmd命苦提示符,輸入命令:
/usr/libexec/java_home4. Error Installing APK
雖然AVD可以從不同的硬體和軟體版本來測試你的app,但你還是要至少在一個物理設備(手機或平板)上測試app。然而AS有時候找不到連接的設備。
如果你將設備連接到電腦上並且安裝apk時報 Error installing APK錯誤,或者設備根本不出現在Select Deployment Target視窗,你可以嘗試如下方法:
檢查USB調試模式是否打開。
具體步驟為,打開設備的設置Settings,點擊開發者選項,打開USB調試。如果設置中沒有開發者選項,那麼可以在 關於手機裡面連續點擊版本號直至出現開發者選項。 再次查看設置,就能看到開發者選項了。
檢查手機或平板的螢幕
當你將設備連接到電腦上的時候有時需要你點擊螢幕來作選擇。比如說要你選擇不同的模式或者授權手機連接到電腦。
確保你的電腦上安裝了設備的驅動。
如果你是在Windows下開發,你可能需要為設備安裝合適的驅動程式。如果你的設備是Nexus,那你可以直接通過AS的SDK管理來下載穀歌驅動。
檢查設備是否滿足你工程的最小SDK要求。
在module的gradle.buil裡面查看最小SDK,然後在設備的設置-關於手機裡面查看安卓版本。
嘗試重啟adb (Android Debug Bridge) 進程
打開終端或cmd,然後切換目錄 (
cd),然後進去platform-tools視窗,比如,cd /Users/Downloads/adt-bundle-mac/sdk/platform-tools按循序執行下麵兩條命令來重啟adb進程:
./adb kill-server./adb start-server假如這些方法都失效,嘗試斷開設備,然後重新連接設備,重啟設備,重啟AS,最後重啟電腦。
5. INSTALL_FAILED_INSUFFICIENT_STORAGE
如果你在嘗試安裝app的時候遇到這個錯誤,這說明你的設備記憶體不足。
如果你嘗試在AVD上面安裝app,那麼你應該檢查你給這個虛擬機器分配了多少記憶體:
該部分列出了分配給虛擬機器的各種類型的記憶體,只有這其中有一個值過低的話,你就應該增大,直到能夠支持典型的安卓手機或平板。下面是各種類型的記憶體:
如果你虛擬機器記憶體足夠的話,或者你正在物理設備上安裝app的話,那麼這個錯誤往往表明你編譯的app太大了。如果一個app在運行的時候佔用設備記憶體太多的話,這不是一件好事情。
假如你需要減少app大小的話,你可以嘗試如下的方法:
使用ProGuard來刪除被使用到的類檔,域,屬性和方法。通過打開module的build.gradle 檔並添加如下代碼,可以打開ProGuard。
buildTypes {
release {
shrinkResources true 來打開資源瘦身功能。android:tint和tintMode屬性,你可以將一幅圖片修改為不同的顏色,通過使用android:fromDegrees, android:toDegrees, android:pivotX和 android:pivotY來旋轉你的圖片。優化你的庫。嘗試刪除你工程中不需要的或者是記憶體密集型的庫。 假如你確實需要一個很大的庫,那麼你可以檢查一下是否存在針對手機的任何可以優化這個庫的方法,因為一般擴展庫的代碼並不是為手機設計的。 還有一點你需要知道的是,許多庫包含大量的當地語系化字串。 如你的app並不支持這些庫,那麼你可以通知Gradle不要將這些字串引入到你編譯的apk中,以此來減小庫的大小。 為了指定你的app官方支援的語言,打開module的 build.gradle 檔並且使用resConfigs屬性。比如說我們只想引入英語字串到我們的工程:
android {
defaultConfig {
resConfigs "en"
考慮一下你的apk是否包含大量的單個用戶下載了但是卻從未使用過的內容。比如說,hdpi螢幕尺寸的手機不會經常使用
xxxhdpiassets! 減小你apk大小的最有效方法之一是將apk分為多個apk,所以當用戶下載你的apk的時候,所以它們下載到的apk裡面的代碼和資源檔只適用於他們的設備。 更多資訊請查看 creating APKs that target different screen densities and specific ABIs(應用二進位介面)安卓官方文檔。
6. ActivityNotFoundException
當你調用startActivity(Intent)或者類似的調用失敗的時候會出現該錯誤,因為Activity無法執行給定的Intent。
大多數情況下出現該錯誤的原因是忘記在設定檔聲明activity,所以打開你的設定檔,檢查你是否聲明了所有的activity。 還要檢查你是否正確的聲明了activity,你可以使用完全合適的類名,或者帶包名的類名。比如說下面兩種情況都是有效的:
<activity android:name=".MainActivity"><activity android:name="com.jessicathornsby.myapplication.MainActivity">果你還是發現不了出現該
問題的原因,也有可能是其他潛在的原因。首先,如果你是將Activity從一個包移到另一個包的時候,那麼你只需要clean and rebuil工程,因為AS搞不清楚了。除此之外,如果
Activity裡面的錯誤沒有被正確的載入,也會出現該錯誤。為了檢查是否是這種情況,使用try-catch代碼塊:try {再次運行app,看看AS的Logcat Monitor視窗是否在創建Activity的時候捕獲了異常。如果是這種情況,解決就行了。
7. ClassCastException
該錯誤與Java的類型轉換機制有關,該機制允許你將變數從一種類型轉換成另外一種類型。 如果你轉換的時候類型不一致,就會出現該錯誤。比如說,下面兩種情況都會導致該錯誤:
Object x = new Integer(0);System.out.println((String)x);一般錯誤資訊都會給出錯誤所在行,所以在工程中定位,找出錯誤解決它。
假如你還不能發現該錯誤,那麼你想一下你最近是否移動了layout資源檔裡面的
View,這是因為曾經有用戶報告在重新佈局View後出現此錯誤。假如你懷疑是這種情況,那麼操作clean/rebuild來告訴AS重新生成資源檔。 這樣就可以強制AS重新註冊layout資源檔裡面的變更,即解決ClassCastException錯誤。8. NullPointerException
在Java中,當你聲明了一個變數,你實際上創建了一個指向某物件的指標。你可以聲明某個物件指向某塊未知數據,並且給該對象分配null值。 null值在編寫設計模式的很有用,但是當你遇到NullPointerException(NPE)錯誤,那麼表示你正在使用指向null值的引用,儘管也是一個物件。 由於該引用指向的地方沒有代碼可以執行,你將以NPE結束。
一般NPE異常會被捕獲,所以Logcat Monitor視窗可以看到錯誤具體在哪行。 在工程中定義到錯誤行,並且找到空引用。然後你需要找到賦值的地方並賦值。
如果請求的
View沒有被找到,findViewById方法也會返回null值,所以如果NPE所在行包含findViewById,那麼檢查是否初始化了包含該View的佈局。還要檢查findViewById裡面的拼寫錯誤或其他小錯誤,這也可能導致NPE錯誤。為了避免你的工程中出現NPE錯誤,確保所有的物件在使用前被初始化了,還要經常驗證使用某物件的方法或屬性不為空。
9. Application Not Responding Error
該錯誤以對話方塊的形式出現在安卓設備或AVD上。如果5秒鐘內未對用戶輸入做出回應就會出現Application Not Responding (ANR) 。 當你的app在UI主執行緒執行冗長的或密集型的操作就會出現ANR。
在安卓裡,UI主執行緒負責將所有的用戶輸入事件分配給合適的UI部件,還負責更新app的UI。然而,主執行緒一次只能處理一個任務,所以假如長時間運行或密集型操作阻塞了主執行緒,那麼UI將完全無回應直到該任務被完成。
假如你在測試app的時候遇到ANR,那麼你需要看看在主執行緒上執行的操作。然而假如你沒有遇到ANR,但是你注意到app有時很慢或很卡,那麼這表明你即將遇到ANR,那麼你應該再次看看你的UI執行緒的狀態。
為了解決ANR錯誤(或者類似的錯誤),你需要檢查所有的操作是否會導致運行減慢或需要大量的性能處理,如果有請將它們從主執行緒移走。即移到一個工作執行緒,在工作執行緒裡面的這些操作就不會阻塞主UI執行緒了。
創建子執行緒有許多方法,其中最簡單的方法是
AsynTask,因為該類已經包含了自己的工作執行緒,和一個回檔,在該回檔裡與安卓UI主執行緒通信。然而AsyncTasks更適合簡短的操作,所以假如你需要執行長時間的操作,那麼你應該使用
Service或者IntentService來代替。儘管從主執行緒移走長時間運行和密集型任務能夠很大程度改善app性能,我們還是盡可能在主執行緒中做更少的工作。即使是在主執行緒運行少量非必需的代碼也會影響app回應速度,所以你一旦找到長時間運行和密集型的操作,你應該看看是否可以從主執行緒中移除更多的代碼。
10. Only the Original Thread That Created a View Hierarchy Can Touch Its Views
在安卓中,你只可以在主執行緒中更新UI。假如你想從其他執行緒中訪問UI元素,那麼你就會遇到這個錯誤。
如果你想解決這個問題,你要找出嘗試更新UI的幕後工作並把它放到
runOnUiThread中,比如:runOnUiThread(new Runnable() {@Overridepublic void run() {或者你可以使用handler或者在AsyncTask中執行後臺工作,使用AsyncTask的
onPostExecute()的回檔與主執行緒通信。最後假如你發現你頻繁切換執行緒,那麼你可能需要RxAndroid,使用該庫你可以創建一個新執行緒,在這個執行緒中制定要操作的工作計畫,然後僅僅幾行代碼就可以把執行結果發送到主執行緒。
11. NetworkOnMainThreadException
當你嘗試在主執行緒執行網路操作的時候拋出該異常,比如說發送API請求,連接遠端資料庫或者下載檔案。由於網路操作耗時並且是勞動密集型,它們更容易阻塞執行緒,所以Android 3.0 (Honeycomb) 和更高版本在你嘗試在主執行緒進行網路操作的時候拋出該異常。
假如你遇到NetworkOnMainThreadException錯誤,那麼找到主執行緒中的網路請求代碼並移到單獨的執行緒。
假如你需要頻繁的進行網路請求,那麼你需要看看Volley,它是一個HTTP庫,初始化自己的後臺執行緒,所以所有的網路請求預設在主執行緒以外進行。
12. Activity Has Leaked Window That Was Originally Added Here
如果你在退出Activity後嘗試顯示對話方塊就會出現該錯誤。如果你遇到此錯誤,那麼打開Activity,確保你正確地關閉對話方塊,正確的做法是在Activity的onDestroy() 或onPause() 方法裡調用dismiss(),比如:
@Overrideprotected void onDestroy()super.onDestroy();if(pDialogue!= null)pDialogue.dismiss();13. OutofMemoryError
當app請求記憶體而系統無法滿足的時候出現該錯誤。假如你遇到這個錯誤,那麼從改掉所有常見沒錯管理開始。檢查你是否反註冊了所有的廣播接收者,是否停止了所有的services,確保你沒有在靜態成員變數裡持有引用,確保你沒有嘗試載入超大bitmaps。
假如你排除了所有導致該錯誤的明顯原因,那麼你需要深挖和仔細檢查app如何分配記憶體,正好趁此機會優化app記憶體管理機制。
AS有整個區域來説明你分析app記憶體使用情況,在AS工具列選擇View > Tools Window。此時你會看到Android Monitor 或者Android Profiler選項,這依賴於你安裝的 AS版本。
我們之前在這個網站討論了 working with the Memory Monitor ,但是由於Android Profiler 是AS新增的特性,所以我們快速介紹下它。
當你打開Android Profiler,它開始自動記錄三方面資訊。
由於我們關心app的記憶體使用情況,點擊Memory,打開Memory Profiler。
Memory Profiler由時間線組成,顯示app當前分配的不同類型的記憶體,比如說Java, native和stack. 。上圖中你會發現一排圖示,可以出發不同的操作:



為了找出app中導致OutOfMemoryError錯誤的地方,花些時間與app交互,監控一下,app在不同的操作時記憶體分配的變化情況。一旦你找到了導致問題的地方,花時間修改好以避免記憶體洩露以及記憶體不足。
在這篇文章中,我們探索了安卓開發中最常見的13個錯誤。我們討論了各種導致該錯誤的所有原因,以及解決錯誤的步驟。
假如困擾你的錯誤在這裡找不到,那麼你首先應該做的是複製粘貼整個錯誤資訊到穀歌,這樣就可以找到別人是怎麼解決這個問題的。
並且,假如你在網上找不到任何解決方法,你可以直接去安卓社區尋求,把你的問題發到Stack Overflow。
下面是安卓開發的其他文章




















