ExceptionReadme
總結(jié)項目中出現(xiàn)的異常
java.util.ConcurrentModificationException
異常描述:該異常表示并發(fā)修改異常.迭代器迭代過程中,迭代的對象發(fā)生了改變,如數(shù)據(jù)項增加或刪除.
解決思路:由于迭代對象不是線程安全,在迭代的過程中,會檢查 modCount 是否和初始 modCount 即 expectedModCount 一致,如果不一致,則認為數(shù)據(jù)有變化,迭代終止并拋出異常。常出現(xiàn)的場景是,兩個線程同時對集合進行操作,線程 1 對集合進行遍歷,而線程 2 對集合進行增加、刪除操作,此時將會發(fā)生 ConcurrentModificationException 異常。
解決方法:多線程訪問時要增加同步鎖,或者建議使用線程安全的集合:
- 使用 ConcurrentHashMap 替換 HashMap,CopyOnWriteArrayList 替換 ArrayList。
- 或者使用使用 Vector 替換 ArrayList,Vector 是線程安全的。Vector 的缺點:大量數(shù)據(jù)操作時,由于線程安全,性能比 ArrayList低。
java.lang.ArrayIndexOutOfBoundsException
異常描述:該異常表示數(shù)組越界.
解決思路:這種情況一般要在數(shù)組循環(huán)前做好 length 判斷,index 超出 length 上限和下限時都會報錯。舉例如下:一個數(shù)組 int test[N],一共有 N 個元素分別是test[0]~test[N-1],如果調(diào)用 test[N],將會報錯。建議讀取時,不要超過數(shù)組的長度(array.length)。
Android 中一種常見情形就是上拉刷新中 header 也會作為 listview 的第 0 個位置,如果判斷失誤很容易造成越界。
異常情況:TextView 中 ellipsize 使用引發(fā) Crash,該問題為 Android 系統(tǒng) bug,存在于 Android 5.0 及以下設(shè)備,問題描述參考
解決方法:使用 android:singleLine="true" 代替 android:lines="1" 和 android:maxLines="1"
java.lang.IllegalStateException
異常描述:狀態(tài)異常
解決思路 錯誤類型大致為以下幾種:
- java.lang.IllegalStateException:Cannot forward a response that is already committed(無法轉(zhuǎn)發(fā)已經(jīng)提交的響應(yīng))
- IllegalStateException:response already commited(響應(yīng)已經(jīng)提交)
- IllegalStateException:getOutputStream() has already been called for this request(已經(jīng)為此請求調(diào)用了getOutputStream )
...............
例1:IllegalStateException: Can not perform this action after onSaveInstanceState(在onSaveInstanceState之后無法執(zhí)行此操作)
錯誤原因:
該異常表示,當前對客戶端的響應(yīng)已經(jīng)結(jié)束,不能在響應(yīng)已經(jīng)結(jié)束(或說消亡)后再向客戶端(實際上是緩沖區(qū))輸出任何內(nèi)容。Object is no longer valid to operate on. Was it deleted by another thread? 該異常表示,realmObject 對象在其他線程已被刪除,在這個線程中使用的時候拋出的異常。解決方法:
onSaveInstanceState 方法是在該 Activity 即將被銷毀前調(diào)用,來保存 Activity 數(shù)據(jù)的,如果在保存玩狀態(tài)后再給它添加 Fragment 就會出錯。解決辦法就是把 commit()方法替換成 commitAllowingStateLoss()
具體分析:
首先解釋下flush(),我們知道在使用讀寫流的時候數(shù)據(jù)先被讀入內(nèi)存這個緩沖區(qū)中, 然后再寫入文件,但是當數(shù)據(jù)讀完時不代表數(shù)據(jù)已經(jīng)寫入文件完畢,因為可能還有一部分仍未寫入文件而留在內(nèi)存中,這時調(diào)用 flush() 方法就會把緩沖區(qū)的數(shù)據(jù)強行清空輸出,因此 flush() 的作用就是保證緩存清空輸出。response 是服務(wù)端對客戶端請求的一個響應(yīng),其中封裝了響應(yīng)頭、狀態(tài)碼、內(nèi)容等,服務(wù)端在把 response 提交到客戶端之前,會向緩沖區(qū)內(nèi)寫入響應(yīng)頭和狀態(tài)碼,然后將所有內(nèi)容 flush。這就標志著該次響應(yīng)已經(jīng) committed。對于當前頁面中已經(jīng) committed 的 response,就不能再使用這個 response 向緩沖區(qū)寫任何東西(注:同一個頁面中的 response.XXX()是同一個 response 的不同方法,只要其中一個已經(jīng)導致了 committed,那么其它類似方式的調(diào)用都會導致 IllegalStateException 異常)。
參考1、參考2
例2: java.lang.IllegalStateException
Can't change tag of fragment d{e183845 #0 d{e183845}}: was d{e183845} now d{e183845 #0 d{e183845}}
錯誤原因:
經(jīng)查,我在顯示 fragment 的代碼中使用了 fragment.show(getSupportFragmentManager, fragment.toString())
而這里是因為兩次 toString()結(jié)果不同,導致不同的 tag 指向的是同一個 fragment。
解決方法:
獲取 fragment 的 tag 的正確方法應(yīng)該是使用其提供的 fragment.getTag()方法。
java.lang.NullPointerException
異常描述:空指針
解決思路:使用了一個空對象引用,建議您檢查引用的對象是否為空
解決方法:這種異常通常是調(diào)用一個對象的方法拋出的,凡是調(diào)用一個對象的方法之前,一定要進行判空或者進行try-catch,這樣基本可以規(guī)避大部分空指針異常。
java.lang.IllegalArgumentException
異常描述:參數(shù)不匹配異常,通常由于傳遞了不正確的參數(shù)導致。
解決思路:檢測傳入的參數(shù)
解決方法:常見于:
- Activity、Service 狀態(tài)異常
- 非法URL
- UI線程操作
- Fragment中嵌套了子 Fragment,F(xiàn)ragment 被銷毀,而內(nèi)部 Fragment 未被銷毀,所以導致再次加載時重復,在 onDestroyView() 中將內(nèi)部 Fragment 銷毀即可
- 在請求網(wǎng)絡(luò)的回調(diào)中使用了glide.into(view),view 已經(jīng)被銷毀會導致該錯誤
android.os.DeadObjectException
異常描述:死機異常
解決思路:建議在服務(wù)終止或?qū)ο蠡厥蘸蟀严鄳?yīng)的引用置空,并且在所有可能用到這個對象的地方進行判空操作。該異常表示對應(yīng)的服務(wù)或?qū)ο笠呀?jīng)停止,但是卻仍有對其發(fā)起調(diào)用。
解決方法:
- 數(shù)據(jù)庫,藍牙等用已調(diào)用過 close()的對象來 connect(),將會報錯。
- 引用被系統(tǒng)回收的對象,也會報這個錯誤。
java.util.concurrent.TimeoutException
異常描述:該異常表示調(diào)用超時。
解決思路:一般是系統(tǒng)在 gc 時,調(diào)用對象的 finalize 超時導致,
解決方法:
- 檢查分析 finalize 的實現(xiàn)為什么耗時較高,修復它。
- 檢查日志查看 GC 是否過于頻繁,導致超時,減少內(nèi)容開銷,防止內(nèi)存泄露。
java.lang.ClassNotFoundException
異常描述:該異常表示在路徑下,找不到指定類,通常是因為構(gòu)建路徑問題導致的。
解決思路:
解決方法:類名是以字符串形式標識的,可信度比較低,在調(diào)用Class.forName(""),Class.findSystemClass(""),Class.loadClass("")等方法時,找不到類名時將會報錯。如果找不到的 Class 是系統(tǒng) Class,那么可能是系統(tǒng)版本兼容,廠家 Rom 兼容的問題,找到對應(yīng)的設(shè)備嘗試重現(xiàn),解決方法可以考慮更換Api,或用自己實現(xiàn)的 Class替代。如果找不到的 Class 是應(yīng)用自由 Class(含第三方SDK的 Class),可以通過反編譯工具查看對應(yīng) apk 中是否真的缺少該 Class,再進行定位,這種往往發(fā)生在:
- 要找的 Class 被混淆了,存在但名字變了。
- 要找的 Class 未被打入 Dex,確實不存在,可能是因為自己的疏忽,或編譯環(huán)境的沖突。
- 要找的 Class 確實存在,但你的 Classlorder 找不到這個 Class,往往因為這個 Classloder 是你自實現(xiàn)的(插件化應(yīng)用中常見)。
java.lang.SecurityException
異常描述:權(quán)限異常
解決思路:檢查權(quán)限
解決方法:權(quán)限異?;蛘叻Q為安全異常,由安全管理器拋出,用于指示違反安全情況的異常,通常由于沒有獲取對應(yīng)的權(quán)限。
- android 6.0 以下需要在 manifest 中聲明相應(yīng)的權(quán)限。
- android 6.0 及以上,在使用時需要動態(tài)申請權(quán)限。
java.lang.OutOfMemoryError
異常描述:該異常表示未能成功分配字節(jié)內(nèi)存,通常是因為內(nèi)存不足導致的內(nèi)存溢出。
解決思路:
解決方法:OOM 就是內(nèi)存溢出,即 Out of Memory。也就是說內(nèi)存占有量超過了 VM 所分配的最大。怎么解決 OOM,通常 OOM 都發(fā)生在需要用到大量內(nèi)存的情況下(創(chuàng)建或解析 Bitmap,分配特大的數(shù)組等),這里列舉常見避免 OOM 的幾個注意點:
- 適當調(diào)整圖像大小。
- 采用合適的緩存策略。
- 采用低內(nèi)存占用量的編碼方式,比如 Bitmap.Config.ARGB_4444 比 Bitmap.Config.ARGB_8888 更省內(nèi)存。
- 及時回收 Bitmap。
- 不要在循環(huán)中創(chuàng)建過多的本地變量。
- 自定義對內(nèi)存分配大小。
- 特殊情況可在 mainfests 的 Application 中增加 android:largeHeap="true" 屬性,比如臨時創(chuàng)建多個小圖片(地圖 marker )
android.os.FileUriExposedException
異常描述:文件 Uri 暴露異常
解決思路:
- 首先我們對 Android N及以上做判斷.
- 然后添加 flags,表明我們要被授予什么樣的臨時權(quán)限.
- BuildConfig.APPLICATION_ID 直接獲取的是應(yīng)用的包名.
解決方法:對于面向 Android N (7.x) 的應(yīng)用,Android 框架執(zhí)行的 StrictMode API 政策禁止向您的應(yīng)用外公開 file://URI。 如果一項包含文件 URI 的 Intent 離開您的應(yīng)用,應(yīng)用失敗,并出現(xiàn) FileUriExposedException 異常。若要在應(yīng)用間共享文件,您應(yīng)發(fā)送一項 content://URI,并授予 URI 臨時訪問權(quán)限。 進行此授權(quán)的最簡單方式是使用 FileProvider 類。參考