摘要:換句話說,環(huán)境或應(yīng)用程序沒有處于請(qǐng)求操作的適當(dāng)狀態(tài)。項(xiàng)目中異常分析引發(fā)崩潰日志的流程分析解決辦法常見的出現(xiàn)場(chǎng)景狀態(tài)異常非法線程操作。導(dǎo)致的方法出來顯示消息位于該消息之后,遲遲沒有執(zhí)行。這時(shí)候,的超時(shí)檢測(cè)結(jié)束,刪除了服務(wù)中的記錄。
目錄介紹
1.1 java.lang.UnsatisfiedLinkError找不到so庫異常
1.2 java.lang.IllegalStateException非法狀態(tài)異常
1.3 android.content.res.Resources$NotFoundException
1.4 java.lang.IllegalArgumentException參數(shù)不匹配異常
1.5 IllegalStateException:Can"t compress a recycled bitmap
1.6 java.lang.NullPointerException空指針異常
1.7 android.view.WindowManager$BadTokenException異常
1.8 java.lang.ClassCastException類轉(zhuǎn)化異常
1.9 Toast運(yùn)行在子線程問題,handler問題
2.1 java.lang.ClassNotFoundException類找不到異常
2.2 java.util.concurrent.TimeoutException連接超時(shí)崩潰
2.3 java.lang.NumberFormatException格式轉(zhuǎn)化錯(cuò)誤
2.4 java.lang.IllegalStateException: Fragment not attached to Activity
2.5 ArrayIndexOutOfBoundsException 角標(biāo)越界異常
2.6 IllegalAccessException 方法中構(gòu)造方法權(quán)限異常
2.7 android.view.WindowManager$BadTokenException,dialog彈窗異常
2.8 java.lang.NoClassDefFoundError 找不到類異常
2.9 Android出現(xiàn):Your project path contains non-ASCII characters.
3.1 OnErrorNotImplementedException【 Can"t create handler inside thread that has not called Looper.prepare()】
3.2 adb.exe,start-server" failed -- run manually if necessary
3.3 java.lang.IllegalStateException: ExpectedBEGIN_OBJECT but was STRING at line 1 column 1 path $
3.4 android.content.ActivityNotFoundException: No Activity found to handle Intent
3.5 Package manager has died導(dǎo)致崩潰
3.6 IllegalArgumentException View添加窗口錯(cuò)誤
3.7 IllegalStateException: Not allowed to start service Intent異常崩潰
3.8 java.lang.IllegalStateException:Can not perform this action after onSaveInstanceState
3.9 在Fragment中通過getActivity找不到上下文,報(bào)null導(dǎo)致空指針異常
4.1 IllegalArgumentException導(dǎo)致崩潰【url地址傳入非法參數(shù),轉(zhuǎn)義字符】
4.2 ClassNotFoundException: Didn"t find class "" on path: /data/app/*錯(cuò)誤
4.3 NoClassDefFoundError異常【該異常表示找不到類定義】
4.4 公司之前項(xiàng)目使用客服udesk,sdk更新后初始化導(dǎo)致崩潰問題
4.5 java.util.concurrent.ExecutionException: com.android.tools.aapt2.Aapt2Exception
4.6 java.util.concurrent.ExecutionException: com.android.ide.common.process.ProcessException
4.7 00768556 /vendor/lib/libllvm-glnext.so [armeabi-v8]無法加載so庫導(dǎo)致崩潰
4.8 Only the original thread that created a view hierarchy can touch its views
4.9 NoSuchMethodException android.support.v4.app.Fragment$InstantiationException
好消息博客筆記大匯總【16年3月到至今】,包括Java基礎(chǔ)及深入知識(shí)點(diǎn),Android技術(shù)博客,Python學(xué)習(xí)筆記等等,還包括平時(shí)開發(fā)中遇到的bug匯總,當(dāng)然也在工作之余收集了大量的面試題,長(zhǎng)期更新維護(hù)并且修正,持續(xù)完善……開源的文件是markdown格式的!同時(shí)也開源了生活博客,從12年起,積累共計(jì)47篇[近20萬字],轉(zhuǎn)載請(qǐng)注明出處,謝謝!
鏈接地址:https://github.com/yangchong2...
如果覺得好,可以star一下,謝謝!當(dāng)然也歡迎提出建議,萬事起于忽微,量變引起質(zhì)變!
1.1 java.lang.UnsatisfiedLinkError
A.詳細(xì)崩潰日志信息
# main(1) java.lang.UnsatisfiedLinkError dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.paidian.hwmc-1/base.apk", dex file "/data/app/com.paidian.hwmc-1/base.apk"],nativeLibraryDirectories=[/data/app/com.paidian.hwmc-1/lib/arm64, /data/app/com.paidian.hwmc-1/base.apk!/lib/arm64-v8a, /vendor/lib64, /system/lib64]]] couldn"t find "libijkffmpeg.so"
B.查看崩潰類信息
這個(gè)異常類的大意是:如果Java虛擬機(jī)找不到聲明為本機(jī)的方法的適當(dāng)本機(jī)語言定義,則引發(fā)。
public class UnsatisfiedLinkError extends LinkageError { private static final long serialVersionUID = -4019343241616879428L; public UnsatisfiedLinkError() { super(); } public UnsatisfiedLinkError(String s) { super(s); } }
C.項(xiàng)目中異常分析
根據(jù)實(shí)際項(xiàng)目可知,當(dāng)準(zhǔn)備播放視頻時(shí),找不到libijkffmpeg.so這個(gè)庫,導(dǎo)致直接崩潰。
D.引發(fā)崩潰日志的流程分析
F.解決辦法
報(bào)這個(gè)錯(cuò)誤通常是so庫加載失敗,或者找不到準(zhǔn)備執(zhí)行的JNI方法:
1.建議檢查so在安裝的過程中是否丟失,沒有放入指定的目錄下;
2.調(diào)用loadLibrary時(shí)檢查是否調(diào)用了正確的so文件名,并對(duì)其進(jìn)行捕獲,進(jìn)行相應(yīng)的處理,防止程序發(fā)生崩潰;
3.檢查下so的架構(gòu)是否跟設(shè)備架構(gòu)一至(如在64-bit架構(gòu)下調(diào)用32-bit的so)。
代碼展示
ndk { //根據(jù)需要 自行選擇添加的對(duì)應(yīng)cpu類型的.so庫。 //abiFilters "armeabi", "armeabi-v7a", "arm64-v8a", "x86", "mips" abiFilters "armeabi-v7a" } dependencies { compile fileTree(dir: "libs", include: ["*.jar"]) //這兩個(gè)是必須要加的,其它的可供選擇 compile "tv.danmaku.ijk.media:ijkplayer-java:0.8.4" compile "tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.4" //其他庫文件 //compile "tv.danmaku.ijk.media:ijkplayer-armv5:0.8.8" //compile "tv.danmaku.ijk.media:ijkplayer-arm64:0.8.8" //compile "tv.danmaku.ijk.media:ijkplayer-x86:0.8.8" //compile "tv.danmaku.ijk.media:ijkplayer-x86_64:0.8.8" }
G.知識(shí)延申
Android 應(yīng)用開發(fā)者應(yīng)該對(duì) UnsatisfiedLinkError 這種類型的錯(cuò)誤比較熟悉了,這個(gè)問題一直困擾著廣大的開發(fā)者,那么有沒有想過有可能你什么都沒做錯(cuò),也會(huì)出現(xiàn)這個(gè)問題呢?
我們?cè)?Android 應(yīng)用開發(fā)測(cè)試過程中曾經(jīng)碰到過這樣的案例,apk 在某機(jī)型上安裝完成之后運(yùn)行即崩潰,報(bào)錯(cuò) UnsatisfiedLinkError。
java.lang.UnsatisfiedLinkError: Couldn’t load mobsec from loader dalvik.system.PathClassLoader.....findLibrary returned null
首先懷疑是在 apk 中相應(yīng)的 libsabi 目錄下沒有放置 libmobsec.so,然而檢查發(fā)現(xiàn)這個(gè) so 在所有的 libsabi 下都有放置過,繼續(xù)排查;
然后的想法是放置的 so 不是對(duì)應(yīng) abi 的,比如由于粗心在 armeabi 目錄下放置了 x86 指令集的 so,導(dǎo)致在 armeabi 指令集手機(jī)上加載出錯(cuò),這個(gè)也被排除掉;
就在沒有頭緒的時(shí)候,想到 System.loadLibrary 函數(shù)加載 so 時(shí),系統(tǒng)是從指定的路徑下加載的,那么這個(gè)路徑下 so 是否存在呢?
我們知道應(yīng)用的私有 Native library 目錄 /data/data/packagename/lib 是一個(gè)符號(hào)鏈接,鏈接到 /data/app-lib/
adb shell 到這個(gè)路徑下,使用命令 ls 查看,果然這個(gè) libmobsec.so 是不存在的。那么是什么原因?qū)е碌哪兀?/p>
分析 Android 系統(tǒng)源碼的實(shí)現(xiàn),發(fā)現(xiàn) /data/app-lib/
A.詳細(xì)崩潰日志信息
onSaveInstanceState方法是在該Activity即將被銷毀前調(diào)用,來保存Activity數(shù)據(jù)的,如果在保存玩狀態(tài)后
再給它添加Fragment就會(huì)出錯(cuò)。
IllegalStateException: Can not perform this action after onSaveInstanceState:
B.查看崩潰類信息
在非法或不適當(dāng)?shù)臅r(shí)間調(diào)用方法的信號(hào)。換句話說,Java環(huán)境或Java應(yīng)用程序沒有處于請(qǐng)求操作的適當(dāng)狀態(tài)。
public class IllegalStateException extends RuntimeException { public IllegalStateException() { super(); } public IllegalStateException(String s) { super(s); } public IllegalStateException(String message, Throwable cause) { super(message, cause); } public IllegalStateException(Throwable cause) { super(cause); } static final long serialVersionUID = -1848914673093119416L; }
C.項(xiàng)目中異常分析
分析
D.引發(fā)崩潰日志的流程分析
F.解決辦法
解決辦法就是把commit()方法替換成 commitAllowingStateLoss()
G.其他延申
錯(cuò)誤類型大致為以下幾種:
java.lang.IllegalStateException:Can"t change tag of fragment d{e183845 #0 d{e183845}}: was d{e183845} now d{e183845 #0 d{e183845}} java.lang.IllegalStateException:Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 37 path $.data
第一種:我在顯示fragment的代碼中使用了:fragment.show(getSupportFragmentManager, fragment.toString());而這里是因?yàn)閮纱蝨oString()結(jié)果不同,導(dǎo)致不同的tag指向的是同一個(gè)fragment。獲取fragment的tag的正確方法應(yīng)該是使用其提供的fragment.getTag()方法。
第二種:該異常是由于服務(wù)器錯(cuò)誤返回的JSON字符串和服務(wù)器正常下時(shí)返回的JSON字符串結(jié)構(gòu)不同,導(dǎo)致利用Gson解析的時(shí)候報(bào)了一個(gè)異常:本該去解析集合卻強(qiáng)制去解析對(duì)象所致.解決辦法:在使用Gson解析JSON時(shí)try cash一下,不報(bào)錯(cuò)按照正常邏輯繼續(xù)解析,報(bào)異常則處理為請(qǐng)求失敗邏輯即可.
1.3 android.content.res.Resources$NotFoundException
A.詳細(xì)崩潰日志信息
Android資源不是可繪制的(顏色或路徑)
Resource is not a Drawable (color or path): TypedValue{t=0x2/d=0x7f040151 a=2} android.view.LayoutInflater.createView(LayoutInflater.java:620)
B.查看崩潰類信息
當(dāng)找不到請(qǐng)求的資源時(shí),資源API將引發(fā)此異常。
public static class NotFoundException extends RuntimeException { public NotFoundException() { } public NotFoundException(String name) { super(name); } public NotFoundException(String name, Exception cause) { super(name, cause); } }
C.項(xiàng)目中異常分析
由于將圖片資源拷貝到了drawable-land-xhdpi目錄下,本來應(yīng)該拷貝到drawable-xhdpi目錄下。
D.引發(fā)崩潰日志的流程分析
F.解決辦法
1.引用的資源ID 是否能匹配到R.java文件中定義的資源;
2.是否因?yàn)榫彺娴仍驅(qū)е戮幾gAPK時(shí)未把資源文件打包進(jìn)去,可以把APK反編譯檢查下;
3.是否使用了一個(gè)錯(cuò)誤的類型來引用了某個(gè)資源或者配置資源時(shí)存在錯(cuò)誤;
4.是否將Int等整型變量作為了參數(shù)傳給了View.setText調(diào)用,這種情況下該整型變量將被認(rèn)為是一個(gè)資源ID號(hào)去資源列表中查找對(duì)應(yīng)的資源,導(dǎo)致找不到對(duì)應(yīng)資源錯(cuò)誤;解決方法是做類型轉(zhuǎn)換View.setText(String.valueOf(Int id))。
1.4 java.lang.IllegalArgumentException參數(shù)不匹配異常A.詳細(xì)崩潰日志信息
B.查看崩潰類信息
參數(shù)不匹配異常,通常由于傳遞了不正確的參數(shù)導(dǎo)致。
public class IllegalArgumentException extends RuntimeException { public IllegalArgumentException() { super(); } public IllegalArgumentException(String s) { super(s); } public IllegalArgumentException(String message, Throwable cause) { super(message, cause); } public IllegalArgumentException(Throwable cause) { super(cause); } private static final long serialVersionUID = -5365630128856068164L; }
C.項(xiàng)目中異常分析
D.引發(fā)崩潰日志的流程分析
F.解決辦法
G.常見的出現(xiàn)場(chǎng)景
Activity、Service狀態(tài)異常;
非法URL;
UI線程操作。
Fragment中嵌套了子Fragment,F(xiàn)ragment被銷毀,而內(nèi)部Fragment未被銷毀,所以導(dǎo)致再次加載時(shí)重復(fù),在onDestroyView() 中將內(nèi)部Fragment銷毀即可
在請(qǐng)求網(wǎng)絡(luò)的回調(diào)中使用了glide.into(view),view已經(jīng)被銷毀會(huì)導(dǎo)致該錯(cuò)誤
1.5 IllegalStateException:Can"t compress a recycled bitmap
A.詳細(xì)崩潰日志信息
無法壓縮回收位圖
Can"t compress a recycled bitmap com.paidian.hwmc.utils.i.a(FileUtils.java:75)
B.查看崩潰類信息
如果位圖已被回收,則希望拋出異常的方法將調(diào)用此值。知道了崩潰的具體位置,就該分析具體的原因呢!
public boolean compress(CompressFormat format, int quality, OutputStream stream) { checkRecycled("Can"t compress a recycled bitmap"); //省略代碼 return result; } //如果位圖已被回收,則希望拋出異常的方法將調(diào)用此值。 private void checkRecycled(String errorMessage) { if (mRecycled) { throw new IllegalStateException(errorMessage); } }
C.項(xiàng)目中異常分析
使用了已經(jīng)被釋放過內(nèi)存的對(duì)象。對(duì)于Bitmap:Bitmap bitmap=一個(gè)bitmap對(duì)象。使用過程中調(diào)用bitmap.recycle(),之后再使用bitmap就會(huì)報(bào)錯(cuò)。
D.引發(fā)崩潰日志的流程分析
bitmap.recycle()解釋如下所示,釋放與此位圖關(guān)聯(lián)的本機(jī)對(duì)象,并清除對(duì)像素?cái)?shù)據(jù)的引用。這將不會(huì)同步釋放像素?cái)?shù)據(jù);它只允許在沒有其他引用的情況下對(duì)其進(jìn)行垃圾收集。位圖被標(biāo)記為“死”,這意味著如果調(diào)用getPixels()或setPixels(),它將拋出異常,而不會(huì)繪制任何內(nèi)容。此操作不能反轉(zhuǎn),因此只有在確定沒有進(jìn)一步使用位圖的情況下才應(yīng)調(diào)用該操作。這是一個(gè)高級(jí)調(diào)用,通常不需要調(diào)用,因?yàn)楫?dāng)沒有對(duì)此位圖的引用時(shí),普通GC進(jìn)程將釋放此內(nèi)存。
Free the native object associated with this bitmap, and clear the reference to the pixel data
F.解決辦法
第一種:在使用bitmap前增加判斷,if (mBitmap.isRecycled()) return null;
1.6 java.lang.NullPointerException空指針異常
A.詳細(xì)崩潰日志信息
Please call the AutoSizeConfig#init() first com.paidian.hwmc.base.BaseApplication.initAutoSizeConfig(BaseApplication.java:386)
B.查看崩潰類信息
空指針異常,也是十分常見的一個(gè)異常
public class NullPointerException extends RuntimeException { private static final long serialVersionUID = 5162710183389028792L; public NullPointerException() { super(); } public NullPointerException(String s) { super(s); } }
C.項(xiàng)目中異常分析
空指針發(fā)生場(chǎng)景較多,是指某一個(gè)對(duì)象報(bào)null,這個(gè)使用去使用它的話就i會(huì)報(bào)該異常。
D.引發(fā)崩潰日志的流程分析
導(dǎo)致出現(xiàn)空指針的原因: 必須滿足兩個(gè)條件才會(huì)發(fā)生空指針:引用變量指向了空,并且調(diào)用了這個(gè)引用的方法
空指針問題解決思路:
查看Log信息看第一行導(dǎo)致空指針發(fā)生的代碼,直接雙擊打開報(bào)空指針的類
查看該行代碼中有幾處調(diào)用了方法,則有幾個(gè)對(duì)象可能是空的,找出哪個(gè)對(duì)象是空的
查看這些對(duì)方在哪里賦值了
如果沒賦值,則給她賦值,問題解決
如果有地方賦值了,則看這個(gè)方法有沒有被調(diào)用(Ctrl + Shift + G)
如果沒有調(diào)用(可能沒調(diào)用或可能調(diào)用時(shí)機(jī)太晚),在使用她前先調(diào)用賦值,問題解決
如果有調(diào)用,則看是不是有其它地方又給她賦值為null了,如果沒有設(shè)置為null,則要看賦值的變量和我們使用時(shí)的變量是否是同一個(gè)變量。
F.解決辦法
空指針最為常見,也最容易規(guī)避,使用的時(shí)候一定要進(jìn)行null check,采取不信任原則:
1.方法形參要判空后才使用;
2.全局變量容易被系統(tǒng)回收或者更改,使用全局變量前建議判空;
3.第三方接口的調(diào)用,對(duì)返回值進(jìn)行判空。
4.請(qǐng)注意線程安全
1.7 android.view.WindowManager$BadTokenException異常,Toast報(bào)錯(cuò)Unable to add window
A.詳細(xì)崩潰日志信息
報(bào)錯(cuò)日志,是不是有點(diǎn)眼熟呀?更多可以看我的開源項(xiàng)目:https://github.com/yangchong211
android.view.WindowManager$BadTokenException Unable to add window -- token android.os.BinderProxy@7f652b2 is not valid; is your activity running?
B.查看崩潰類信息
查詢報(bào)錯(cuò)日志是從哪里來的
C.項(xiàng)目中異常分析
D.引發(fā)崩潰日志的流程分析
這個(gè)異常發(fā)生在Toast顯示的時(shí)候,原因是因?yàn)閠oken失效。通常情況下,一般是不會(huì)出現(xiàn)這種異常。但是由于在某些情況下, Android進(jìn)程某個(gè)UI線程的某個(gè)消息阻塞。導(dǎo)致 TN 的 show 方法 post 出來 0 (顯示) 消息位于該消息之后,遲遲沒有執(zhí)行。這時(shí)候,NotificationManager 的超時(shí)檢測(cè)結(jié)束,刪除了 WMS 服務(wù)中的 token 記錄。刪除 token 發(fā)生在 Android 進(jìn)程 show 方法之前。這就導(dǎo)致了上面的異常。
測(cè)試代碼。模擬一下異常的發(fā)生場(chǎng)景,其實(shí)很容易,只需要這樣做就可以出現(xiàn)上面這個(gè)問題
Toast.makeText(this,"瀟湘劍雨-yc",Toast.LENGTH_SHORT).show(); try { Thread.sleep(20000); } catch (InterruptedException e) { e.printStackTrace(); }
F.解決辦法
目前見過好幾種,思考一下那種比較好……
第一種,既然是報(bào)is your activity running,那可以不可以在吐司之前先判斷一下activity是否running呢?
第二種,拋出異常增加try-catch,代碼如下所示,最后仍然無法解決問題
按照源碼分析,異常是發(fā)生在下一個(gè)UI線程消息中,因此在上一個(gè)ui線程消息中加入try-catch是沒有意義的。而且用到吐司地方這么多,這樣做也不方便啦!
第三種,那就是自定義類似吐司Toast的view控件。個(gè)人建議除非要求非常高,不然不要這樣做。畢竟發(fā)生這種異常還是比較少見的
G.哪些情況會(huì)發(fā)生該問題?
UI 線程執(zhí)行了一條非常耗時(shí)的操作,比如加載圖片等等,就類似上面用 sleep 模擬情況
進(jìn)程退后臺(tái)或者息屏了,系統(tǒng)為了減少電量或者某種原因,分配給進(jìn)程的cpu時(shí)間減少,導(dǎo)致進(jìn)程內(nèi)的指令并不能被及時(shí)執(zhí)行,這樣一樣會(huì)導(dǎo)致進(jìn)程看起來”卡頓”的現(xiàn)象
當(dāng)TN拋出消息的時(shí)候,前面有大量的 UI 線程消息等待執(zhí)行,而每個(gè) UI 線程消息雖然并不卡頓,但是總和如果超過了 NotificationManager 的超時(shí)時(shí)間,還是會(huì)出現(xiàn)問題
1.8 java.lang.ClassCastException類轉(zhuǎn)化異常
A.詳細(xì)崩潰日志信息
android.widget.FrameLayout cannot be cast to android.widget.RelativeLayout com.paidian.hwmc.goods.activity.GoodsDetailsActivity.initView(GoodsDetailsActivity.java:712)
B.查看崩潰類信息
拋出以指示代碼試圖將對(duì)象強(qiáng)制轉(zhuǎn)換為它不是實(shí)例的子類。
public class ClassCastException extends RuntimeException { private static final long serialVersionUID = -9223365651070458532L; public ClassCastException() { super(); } public ClassCastException(String s) { super(s); } }
C.項(xiàng)目中異常分析
該異常表示類型轉(zhuǎn)換異常,通常是因?yàn)橐粋€(gè)類對(duì)象轉(zhuǎn)換為其他不兼容類對(duì)象拋出的異常,檢查你要轉(zhuǎn)換的類對(duì)象類型。
D.引發(fā)崩潰日志的流程分析
F.解決辦法
一般在強(qiáng)制類型轉(zhuǎn)換時(shí)出現(xiàn),例如如果A向B轉(zhuǎn)換,而A不是B的父類時(shí),將產(chǎn)生java.lang.ClassCastException異常。一般建議做這時(shí)要使用instanceof做一下類型判斷,再做轉(zhuǎn)換。
該案例中,需要把FrameLayout更改成RelativeLayout就可以呢
1.9 Toast運(yùn)行在子線程問題,handler問題
A.詳細(xì)崩潰日志信息
先來看看問題代碼,會(huì)出現(xiàn)什么問題呢?
new Thread(new Runnable() { @Override public void run() { ToastUtils.showRoundRectToast("瀟湘劍雨-楊充"); } }).start();
報(bào)錯(cuò)日志如下所示:
然后找找報(bào)錯(cuò)日志從哪里來的
![image]()
子線程中吐司的正確做法,代碼如下所示
new Thread(new Runnable() { @Override public void run() { Looper.prepare(); ToastUtils.showRoundRectToast("瀟湘劍雨-楊充"); Looper.loop(); } }).start();
得出的結(jié)論
Toast也可以在子線程執(zhí)行,不過需要手動(dòng)提供Looper環(huán)境的。
Toast在調(diào)用show方法顯示的時(shí)候,內(nèi)部實(shí)現(xiàn)是通過Handler執(zhí)行的,因此自然是不阻塞Binder線程,另外,如果addView的線程不是Loop線程,執(zhí)行完就結(jié)束了,當(dāng)然就沒機(jī)會(huì)執(zhí)行后續(xù)的請(qǐng)求,這個(gè)是由Hanlder的構(gòu)造函數(shù)保證的。可以看看handler的構(gòu)造函數(shù),如果Looper==null就會(huì)報(bào)錯(cuò),而Toast對(duì)象在實(shí)例化的時(shí)候,也會(huì)為自己實(shí)例化一個(gè)Hanlder,這就是為什么說“一定要在主線程”,其實(shí)準(zhǔn)確的說應(yīng)該是 “一定要在Looper非空的線程”。
Handler的構(gòu)造函數(shù)如下所示:
2.1 java.lang.ClassNotFoundException類找不到異常
A.詳細(xì)崩潰日志信息
Didn"t find class "om.scwang.smartrefresh.layout.SmartRefreshLayout" on path: DexPathList[[zip file "/data/app/com.paidian.hwmc-EsIbVq6e0mFwE0-rPanqdg==/base.apk", zip file "/data/app/com.paidian.hwmc-EsIbVq6e0mFwE0-rPanqdg==/split_lib_dependencies_apk.apk", zip file "/data/app/com.paidian.hwmc-EsIbVq6e0mFwE0-rPanqdg==/split_lib_slice_0_apk.apk", zip file "/data/app/com.paidian.hwmc-EsIbVq6e0mFwE0-rPanqdg==/split_lib_slice_1_apk.apk", zip file "/data/app/com.paidian.hwmc-EsIbVq6e0mFwE0-rPanqdg==/split_lib_s com.paidian.hwmc.goods.activity.GoodsDetailsActivity.onCreate(GoodsDetailsActivity.java:209)
B.查看崩潰類信息
當(dāng)應(yīng)用程序嘗試使用字符串名稱加載類時(shí)引發(fā):但無法找到具有指定名稱的類的定義。從1.4版開始,已對(duì)此異常進(jìn)行了修改,以符合通用的異常鏈接機(jī)制。在構(gòu)建時(shí)提供并通過{@link#getException()}方法訪問的“在加載類時(shí)引發(fā)的可選異常”現(xiàn)在稱為原因,并且可以通過{@link Throwable#getCace()}方法以及前面提到的“遺留方法”進(jìn)行訪問。
public class ClassNotFoundException extends ReflectiveOperationException { private static final long serialVersionUID = 9176873029745254542L; private Throwable ex; public ClassNotFoundException() { super((Throwable)null); // Disallow initCause } public ClassNotFoundException(String s) { super(s, null); // Disallow initCause } public ClassNotFoundException(String s, Throwable ex) { super(s, null); // Disallow initCause this.ex = ex; } public Throwable getException() { return ex; } public Throwable getCause() { return ex; } }
C.項(xiàng)目中異常分析
該異常表示在路徑下,找不到指定類,通常是因?yàn)闃?gòu)建路徑問題導(dǎo)致的。
D.引發(fā)崩潰日志的流程分析
F.解決辦法
類名是以字符串形式標(biāo)識(shí)的,可信度比較低,在調(diào)用Class.forName(""),Class.findSystemClass(""),Class.loadClass("")等方法時(shí),找不到類名時(shí)將會(huì)報(bào)錯(cuò)。如果找不到的Class是系統(tǒng)Class,那么可能是系統(tǒng)版本兼容,廠家Rom兼容的問題,找到對(duì)應(yīng)的設(shè)備嘗試重現(xiàn),解決方法可以考慮更換Api,或用自己實(shí)現(xiàn)的Class替代。
如果找不到的Class是應(yīng)用自由Class(含第三方SDK的Class),可以通過反編譯工具查看對(duì)應(yīng)apk中是否真的缺少該Class,再進(jìn)行定位,這種往往發(fā)生在:
1.要找的Class被混淆了,存在但名字變了;
2.要找的Class未被打入Dex,確實(shí)不存在,可能是因?yàn)樽约旱氖韬觯蚓幾g環(huán)境的沖突;
3.要找的Class確實(shí)存在,但你的Classlorder找不到這個(gè)Class,往往因?yàn)檫@個(gè)Classloder是你自實(shí)現(xiàn)的(插件化應(yīng)用中常見)。
G.其他延申
2.2 java.util.concurrent.TimeoutException連接超時(shí)崩潰
A.詳細(xì)崩潰日志信息
java.util.concurrent.TimeoutException: android.view.ThreadedRenderer.finalize() timed out after 10 seconds at android.view.ThreadedRenderer.nDeleteProxy(Native Method) at android.view.ThreadedRenderer.finalize(ThreadedRenderer.java:423)
B.查看崩潰類信息
當(dāng)阻塞操作超時(shí)引發(fā)的異常。指定超時(shí)的阻塞操作需要一種方法來指示已發(fā)生超時(shí)。對(duì)于許多此類操作,可以返回指示超時(shí)的值;如果不可能或不需要,則應(yīng)聲明并拋出{@code TimeoutException}。
public class TimeoutException extends Exception { private static final long serialVersionUID = 1900926677490660714L; public TimeoutException() {} public TimeoutException(String message) { super(message); } }
C.項(xiàng)目中異常分析
D.引發(fā)崩潰日志的流程分析
F.解決辦法
一般是系統(tǒng)在gc時(shí),調(diào)用對(duì)象的finalize超時(shí)導(dǎo)致,解決辦法:
1.檢查分析finalize的實(shí)現(xiàn)為什么耗時(shí)較高,修復(fù)它;
2.檢查日志查看GC是否過于頻繁,導(dǎo)致超時(shí),減少內(nèi)容開銷,防止內(nèi)存泄露。
G.其他延申
2.3 java.lang.NumberFormatException格式轉(zhuǎn)化錯(cuò)誤
A.詳細(xì)崩潰日志信息
Exception in thread "main" java.lang.NumberFormatException: For input string: "100 " at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48) at java.lang.Integer.parseInt(Integer.java:458) at java.lang.Integer.parseInt(Integer.java:499)
B.查看崩潰類信息
引發(fā),以指示應(yīng)用程序試圖將字符串轉(zhuǎn)換為數(shù)字類型之一,但該字符串沒有適當(dāng)?shù)母袷健?/p>
public class NumberFormatException extends IllegalArgumentException { static final long serialVersionUID = -2848938806368998894L; public NumberFormatException () { super(); } public NumberFormatException (String s) { super (s); } static NumberFormatException forInputString(String s) { return new NumberFormatException("For input string: "" + s + """); } }
C.項(xiàng)目中異常分析
錯(cuò)誤關(guān)鍵字 java.lang.NumberFormatException 這句話明確告訴了我們是數(shù)字格式異常,接著后面有 For input string: "100 " 提示,這就告訴我們,當(dāng)前想把 "100 " 轉(zhuǎn)換成數(shù)字類型時(shí)出錯(cuò)了,這樣就很確切了。
D.引發(fā)崩潰日志的流程分析
F.解決辦法
解決辦法很簡(jiǎn)單,改成 Integer.parseInt(str.trim()),注意將字符串轉(zhuǎn)化成整數(shù)數(shù)據(jù)類型時(shí),注意需要trim一下。
G.其他延申
2.4 java.lang.IllegalStateException: Fragment not attached to Activity
A.詳細(xì)崩潰日志信息
java.lang.IllegalStateException: Fragment not attached to Activity
B.查看崩潰類信息
C.項(xiàng)目中異常分析
出現(xiàn)該異常,是因?yàn)镕ragment的還沒有Attach到Activity時(shí),調(diào)用了如getResource()等,需要上下文Content的函數(shù)。
出現(xiàn)該異常,是因?yàn)镕ragment還沒有Attach到Activity時(shí),調(diào)用了如getResource()等,需要上下文Context的函數(shù)。解決方法,就是等將調(diào)用的代碼寫在OnStart()中。
D.引發(fā)崩潰日志的流程分析
F.解決辦法
將調(diào)用的代碼運(yùn)行在Fragment Attached的生命周期內(nèi)。
第一種:在調(diào)用需要Context的函數(shù)之前,增加一個(gè)判斷isAdded()
if(isAdded()){//isAdded方法是Android系統(tǒng)提供的,只有在Fragment被添加到所屬的Activity后才返回true activity.getResourses().getString(...); }
第二種:如下所示
@Override public void onAttach(Context context) { super.onAttach(context); activity = (MainActivity) context; } @Override public void onDetach() { super.onDetach(); if (activity != null) { activity = null; } }
G.其他延申
發(fā)生場(chǎng)景:該錯(cuò)誤經(jīng)常發(fā)生在fragment的線程中執(zhí)行了一個(gè)耗時(shí)操作,線程在執(zhí)行完畢后會(huì)調(diào)用getResources來更新ui。如果在線程操作沒有完成,就調(diào)用getActivity().recreate()重新加載activity或屏幕旋轉(zhuǎn),這時(shí)就會(huì)出現(xiàn)Fragment not attached to Activity的錯(cuò)誤
2.5 ArrayIndexOutOfBoundsException 角標(biāo)越界異常
A.詳細(xì)崩潰日志信息
該異常表示數(shù)組越界
java.lang.ArrayIndexOutOfBoundsException: 0 at com.example.mytest.CityAdapter.setDataNotify(CityAdapter.java:183) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
B.查看崩潰類信息
引發(fā),以指示已使用非法索引訪問數(shù)組。索引不是負(fù)的,就是大于或等于數(shù)組的大小。
public class ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException { private static final long serialVersionUID = -5116101128118950844L; public ArrayIndexOutOfBoundsException() { super(); } public ArrayIndexOutOfBoundsException(int index) { super("Array index out of range: " + index); } public ArrayIndexOutOfBoundsException(String s) { super(s); } public ArrayIndexOutOfBoundsException(int sourceLength, int index) { super("length=" + sourceLength + "; index=" + index); } public ArrayIndexOutOfBoundsException(int sourceLength, int offset, int count) { super("length=" + sourceLength + "; regionStart=" + offset + "; regionLength=" + count); } }
C.項(xiàng)目中異常分析
D.引發(fā)崩潰日志的流程分析
F.解決辦法
這種情況一般要在數(shù)組循環(huán)前做好length判斷,index超出length上限和下限時(shí)都會(huì)報(bào)錯(cuò)。舉例如下:一個(gè)數(shù)組int test[N],一共有N個(gè)元素分別是test[0]~test[N-1],如果調(diào)用test[N],將會(huì)報(bào)錯(cuò)。建議讀取時(shí),不要超過數(shù)組的長(zhǎng)度(array.length)。
Android中一種常見情形就是上拉刷新中header也會(huì)作為listview的第0個(gè)位置,如果判斷失誤很容易造成越界。
G.其他延申
2.6 IllegalAccessException 方法中構(gòu)造方法權(quán)限異常
A.詳細(xì)崩潰日志信息
Unable to instantiate application com.pedaily.yc.meblurry.App: java.lang.IllegalAccessException
B.查看崩潰類信息
當(dāng)應(yīng)用程序試圖反射地創(chuàng)建實(shí)例(數(shù)組除外)、設(shè)置或獲取字段或調(diào)用方法時(shí),將引發(fā)IllegalAccessException,但當(dāng)前執(zhí)行的方法無法訪問指定的類、字段、方法或構(gòu)造函數(shù)的定義。
public class IllegalAccessException extends ReflectiveOperationException { private static final long serialVersionUID = 6616958222490762034L; public IllegalAccessException() { super(); } public IllegalAccessException(String s) { super(s); } }
C.項(xiàng)目中異常分析
錯(cuò)誤提示是,構(gòu)造方法的權(quán)限不對(duì)
D.引發(fā)崩潰日志的流程分析
F.解決辦法
檢查了整個(gè)Application,才發(fā)現(xiàn),原來有一個(gè)無參數(shù)的構(gòu)造方法,被設(shè)計(jì)成private。修改其為public即可。
G.其他延申
android BroadcastReceiver遇到j(luò)ava.lang.IllegalAccessException解決方法,錯(cuò)誤原因主要是app中其他地方調(diào)用了默認(rèn)的構(gòu)造函數(shù),必須增加默認(rèn)構(gòu)造函數(shù)且訪問權(quán)限為public
2.7 android.view.WindowManager$BadTokenException,dialog彈窗異常
A.詳細(xì)崩潰日志信息
Unable to add window -- token android.os.BinderProxy@9a57804 is not valid; is your activity running? android.view.ViewRootImpl.setView(ViewRootImpl.java:907)
B.查看崩潰類信息
在WindowManager中可以找到這個(gè)異常類,主要發(fā)生在嘗試添加視圖時(shí)引發(fā)的
public static class BadTokenException extends RuntimeException { public BadTokenException() { } public BadTokenException(String name) { super(name); } }
C.項(xiàng)目中異常分析
該異常表示不能添加窗口,通常是所要依附的view已經(jīng)不存在導(dǎo)致的。
D.引發(fā)崩潰日志的流程分析
F.解決辦法
之前項(xiàng)目中有一個(gè)自定義彈窗,偶爾會(huì)報(bào)這個(gè)錯(cuò)。解決辦法如下代碼所示
主要邏輯是在彈窗show或者dismiss的時(shí)候,都增加了邏輯判斷,判斷宿主activity存在。
/** * 展示加載窗 * @param context 上下文 * @param isCancel 是否可以取消 */ public static void show(Context context, boolean isCancel) { if(context == null){ return; } if (context instanceof Activity) { if (((Activity) context).isFinishing()) { return; } } if (loadDialog != null && loadDialog.isShowing()) { return; } loadDialog = new LoadLayoutDialog(context, isCancel); loadDialog.show(); } /** * 銷毀加載窗 * @param context 上下文 */ public static void dismiss(Context context) { if(context == null){ return; } try { if (context instanceof Activity) { if (((Activity) context).isFinishing()) { loadDialog = null; return; } } if (loadDialog != null && loadDialog.isShowing()) { Context loadContext = loadDialog.getContext(); if (loadContext instanceof Activity) { if (((Activity) loadContext).isFinishing()) { loadDialog = null; return; } } loadDialog.dismiss(); loadDialog = null; } } catch (Exception e) { e.printStackTrace(); loadDialog = null; } }
G.其他延申
Dialog&AlertDialog,Toast,WindowManager不能正確使用時(shí),經(jīng)常會(huì)報(bào)出該異常,原因比較多,幾個(gè)常見的場(chǎng)景如下:
1.上一個(gè)頁面沒有destroy的時(shí)候,之前的Activity已經(jīng)接收到了廣播。如果此時(shí)之前的Activity進(jìn)行UI層面的操作處理,就會(huì)造成crash。UI層面的刷新,一定要注意時(shí)機(jī),建議使用set_result來代替廣播的形式進(jìn)行刷新操作,避免使用廣播的方式,代碼不直觀且容易出錯(cuò)。
2.Dialog在Actitivty退出后彈出。在Dialog調(diào)用show方法進(jìn)行顯示時(shí),必須要有一個(gè)Activity作為窗口的載體,如果Activity被銷毀,那么導(dǎo)致Dialog的窗口載體找不到。建議在Dialog調(diào)用show方法之前先判斷Activity是否已經(jīng)被銷毀。
3.Service&Application彈出對(duì)話框或WindowManager添加view時(shí),沒有設(shè)置window type為TYPE_SYSTEM_ALERT。需要在調(diào)用dialog.show()方法前添加dialog.getWindow().SetType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)。
4.6.0的系統(tǒng)上, (非定制 rom 行為)若沒有給予懸浮窗權(quán)限, 會(huì)彈出該問題, 可以通過Settings.canDrawOverlays來判斷是否有該權(quán)限.
5.某些不穩(wěn)定的MIUI系統(tǒng)bug引起的權(quán)限問題,系統(tǒng)把Toast也當(dāng)成了系統(tǒng)級(jí)彈窗,android6.0的系統(tǒng)Dialog彈窗需要用戶手動(dòng)授權(quán),若果app沒有加入SYSTEM_ALERT_WINDOW權(quán)限就會(huì)報(bào)這個(gè)錯(cuò)。需要加入給app加系統(tǒng)Dialog彈窗權(quán)限,并動(dòng)態(tài)申請(qǐng)權(quán)限,不滿足第一條會(huì)出現(xiàn)沒權(quán)限閃退,不滿足第二條會(huì)出現(xiàn)沒有Toast的情況。
H.其他建議
1.不要在非UI線程中使用對(duì)話框創(chuàng)建,顯示和取消對(duì)話框;
2.盡量少用多帶帶線程,出發(fā)是真正的耗時(shí)操作采用線程,線程也不要直接用Java式的匿名線程,除非是那種單純的操作,操作完成不需要做其他事情的。
3.如果是在fragment中發(fā)起異步網(wǎng)絡(luò)的回調(diào)中進(jìn)行dialog的操作,那么在操作之前,需要判斷 isAdd( ),避免fragment被回收了但是還要求dialog去dismiss
4.在Activity onDestroy中對(duì)Dialog提前進(jìn)行關(guān)閉
2.8 java.lang.NoClassDefFoundError 找不到類異常A.詳細(xì)崩潰日志信息
B.查看崩潰類信息
如果Java虛擬機(jī)或ClassLoader實(shí)例試圖加載類的定義(作為普通方法調(diào)用的一部分或使用新的表達(dá)式創(chuàng)建新實(shí)例的一部分),則拋出該類的定義。編譯當(dāng)前執(zhí)行的類時(shí)存在搜索類定義,但無法再找到該定義。
public class NoClassDefFoundError extends LinkageError { private static final long serialVersionUID = 9095859863287012458L; public NoClassDefFoundError() { super(); } public NoClassDefFoundError(String s) { super(s); } private NoClassDefFoundError(String detailMessage, Throwable throwable) { super(detailMessage, throwable); } }
C.項(xiàng)目中異常分析
問題的主要原因:方法數(shù)超65536限制。由于實(shí)際開發(fā)當(dāng)中的需求不斷變更,開源框架越來越多,大多都用第三方SDK,導(dǎo)致方法數(shù)很容易超出65536限制。出現(xiàn)錯(cuò)誤Java.lang.NoClassDefFoundError
D.引發(fā)崩潰日志的流程分析
這個(gè)錯(cuò)誤是Android應(yīng)用的方法總數(shù)限制造成的。android平臺(tái)的Java虛擬機(jī)Dalvik在執(zhí)行DEX格式的Java應(yīng)用程序時(shí),使用原生類型short來索引DEX文件中的方法。這意味著單個(gè)DEX文件可被引用的方法總數(shù)被限制為65536。通常APK包含一個(gè)classes.dex文件,因此Android應(yīng)用的方法總數(shù)不能超過這個(gè)數(shù)量,這包括Android框架、類庫和你自己開發(fā)的代碼。而Android 5.0和更高版本使用名為ART的運(yùn)行時(shí),它原生支持從APK文件加載多個(gè)DEX文件。在應(yīng)用安裝時(shí),它會(huì)執(zhí)行預(yù)編譯,掃描classes(..N).dex文件然后將其編譯成單個(gè).oat文件用于執(zhí)行. 通熟的講,就是分包。
F.解決辦法
64k解決辦法
G.其他延申
該異常表示找不到類定義,當(dāng)JVM或者ClassLoader實(shí)例嘗試裝載該類的定義(這通常是一個(gè)方法調(diào)用或者new表達(dá)式創(chuàng)建一個(gè)實(shí)例過程的一部分)而這個(gè)類定義并沒有找時(shí)所拋出的錯(cuò)誤。
[解決方案]:NoClassDefFoundError異常一般出現(xiàn)在編譯環(huán)境和運(yùn)行環(huán)境不一致的情況下,就是說有可能在編譯過后更改了Classpath或者jar包所以導(dǎo)致在運(yùn)行的過程中JVM或者ClassLoader無法找到這個(gè)類的定義。
1.分dex包編程,如果依賴的dex包刪除了指定的類,執(zhí)行初始化方法時(shí)將會(huì)報(bào)錯(cuò);
2.使用第三方SDK或插件化編程時(shí),動(dòng)態(tài)加載或?qū)嵗愂?huì)報(bào)錯(cuò);
3.系統(tǒng)資源緊張時(shí),當(dāng)大量class需要加載到內(nèi)存的時(shí)候,處于競(jìng)爭(zhēng)關(guān)系,部分calss競(jìng)爭(zhēng)失敗,導(dǎo)致加載不成功;
4.裝載并初始化一個(gè)類時(shí)失?。ū热珈o態(tài)塊拋 java.lang.ExceptionInInitializerError 異常),然后再次引用此類也會(huì)提示NoClassDefFoundErr 錯(cuò)誤;
5.手機(jī)系統(tǒng)版本或硬件設(shè)備不匹配(如ble設(shè)備只支持18以上SDK),程序引用的class在低版本中不存在,導(dǎo)致NoClassDefFoundErr 錯(cuò)誤。
6.so文件找不到,設(shè)備平臺(tái)armeabi-v7a,但是我的so庫是放在armeabi中的,解決方法新建一個(gè)armeabi-v7a包,并且把a(bǔ)rmeabi的文件拷貝過來.
2.9 Android出現(xiàn):Your project path contains non-ASCII characters.A.詳細(xì)崩潰日志信息
B.查看崩潰類信息
C.項(xiàng)目中異常分析
D.引發(fā)崩潰日志的流程分析
F.解決辦法
很好解決啦,就是你的工程項(xiàng)目路徑或者項(xiàng)目名稱包含了中文,修改相關(guān)的名稱就好
G.其他延申
3.1 OnErrorNotImplementedException【 Can"t create handler inside thread that has not called Looper.prepare()】
A.詳細(xì)崩潰日志信息
Can"t create handler inside thread that has not called Looper.prepare()
B.查看崩潰類信息
C.項(xiàng)目中異常分析
D.引發(fā)崩潰日志的流程分析
這是因?yàn)镠andler對(duì)象與其調(diào)用者在同一線程中,如果在Handler中設(shè)置了延時(shí)操作,則調(diào)用線程也會(huì)堵塞。每個(gè)Handler對(duì)象都會(huì)綁定一個(gè)Looper對(duì)象,每個(gè)Looper對(duì)象對(duì)應(yīng)一個(gè)消息隊(duì)列(MessageQueue)。如果在創(chuàng)建Handler時(shí)不指定與其綁定的Looper對(duì)象,系統(tǒng)默認(rèn)會(huì)將當(dāng)前線程的Looper綁定到該Handler上。
在主線程中,可以直接使用new Handler()創(chuàng)建Handler對(duì)象,其將自動(dòng)與主線程的Looper對(duì)象綁定;在非主線程中直接這樣創(chuàng)建Handler則會(huì)報(bào)錯(cuò),因?yàn)锳ndroid系統(tǒng)默認(rèn)情況下非主線程中沒有開啟Looper,而Handler對(duì)象必須綁定Looper對(duì)象。
如果在主線程中創(chuàng)建handler時(shí),系統(tǒng)會(huì)自動(dòng)創(chuàng)建Looper,但是在子線程中創(chuàng)建handler時(shí),是不會(huì)自動(dòng)創(chuàng)建Looper的,此時(shí)如果不手動(dòng)創(chuàng)建Looper,系統(tǒng)就會(huì)崩潰
F.解決辦法
不要在子線程中做UI操作,比如更改界面,吐司等等……
方法1:需先在該線程中手動(dòng)開啟Looper(Looper.prepare()-->Looper.loop()),然后將其綁定到Handler對(duì)象上;
final Runnable runnable = new Runnable() { @Override public void run() { //執(zhí)行耗時(shí)操作 try { Log.e("bm", "runnable線程: " + Thread.currentThread().getId()+ " name:" + Thread.currentThread().getName()); Thread.sleep(2000); Log.e("bm", "執(zhí)行完耗時(shí)操作了~"); } catch (InterruptedException e) { e.printStackTrace(); } } }; new Thread() { public void run() { Looper.prepare(); new Handler().post(runnable);//在子線程中直接去new 一個(gè)handler Looper.loop(); //這種情況下,Runnable對(duì)象是運(yùn)行在子線程中的,可以進(jìn)行聯(lián)網(wǎng)操作,但是不能更新UI } }.start();
方法2:通過Looper.getMainLooper(),獲得主線程的Looper,將其綁定到此Handler對(duì)象上。
final Runnable runnable = new Runnable() { @Override public void run() { //執(zhí)行耗時(shí)操作 try { Log.e("bm", "runnable線程: " + Thread.currentThread().getId()+ " name:" + Thread.currentThread().getName()); Thread.sleep(2000); Log.e("bm", "執(zhí)行完耗時(shí)操作了~"); } catch (InterruptedException e) { e.printStackTrace(); } } }; new Thread() { public void run() { //在子線程中直接去new 一個(gè)handler new Handler(Looper.getMainLooper()).post(runnable); //這種情況下,Runnable對(duì)象是運(yùn)行在主線程中的,不可以進(jìn)行聯(lián)網(wǎng)操作,但是可以更新UI } }.start();
G.其他延申
3.2 platform-toolsadb.exe,start-server" failed -- run manually if necessaryA.詳細(xì)崩潰日志信息
B.查看崩潰類信息
C.項(xiàng)目中異常分析
adb啟動(dòng)失敗,端口被占用
D.引發(fā)崩潰日志的流程分析
F.解決辦法
百度google大家多說的是任務(wù)管理器 kill掉adb 或者重啟adb server,但我任務(wù)管理器就沒有adb ,猜測(cè)是某個(gè)程序占用了adb端口。于是按此思路查找。 5037為adb默認(rèn)端口 查看該端口情況如下: netstat -aon|findstr "5037" TCP 127.0.0.1:5037 0.0.0.0:0 LISTENING 6540 發(fā)現(xiàn)6540占用了 5037端口,繼續(xù)查看6540的task,發(fā)現(xiàn)是wandoujia .如下所示 tasklist|findstr "6540" wandoujia_daemon.exe 6540 Console 1 4,276 K 接下來問題就好解決了,在任務(wù)管理器kill掉wandoujia_daemon.exe ,運(yùn)行android程序,ok . 1.關(guān)閉xx莢進(jìn)程 2.adb kill-server 3.adb start-server
G.其他延申
3.3 java.lang.IllegalStateException: ExpectedBEGIN_OBJECT but was STRING at line 1 column 1 path $
A.詳細(xì)崩潰日志信息
非法參數(shù),開始讀取時(shí)應(yīng)該是{}括號(hào),所以需要處理String字符串,它有可能不是標(biāo)準(zhǔn)的json數(shù)據(jù)
java.lang.IllegalStateException: ExpectedBEGIN_OBJECT but was STRING at line 1 column 1 path $
B.查看崩潰類信息
C.項(xiàng)目中異常分析
Gson解析數(shù)據(jù)出現(xiàn)問題,原因服務(wù)器返回?cái)?shù)據(jù)不嚴(yán)謹(jǐn)
D.引發(fā)崩潰日志的流程分析
可能的錯(cuò)誤:
bean類字段類型和字段名稱不一致。
服務(wù)器訪問得到的字符串不是純json前面有空格和回車等字符(難發(fā)現(xiàn))。
如果訪問的json字符串不是utf-8編碼時(shí),用Gson解析會(huì)出這種問題,在日志中打印會(huì)發(fā)現(xiàn)json的{}前面有亂碼字符,也需要注意一下。這是因?yàn)椴煌木幋a的原因?qū)е碌模虼吮仨氃L問utf-8的json字符串,才會(huì)減少這種問題。
問題可能是:字符串并不是純json字符串,開頭可能會(huì)帶有空字符或者回車符,這屬于服務(wù)器問題,但我們也可以解決。
最重要原因的我們網(wǎng)絡(luò)請(qǐng)求后結(jié)果是字符串,而不是json,因此需要處理。
F.解決辦法
/**
*/ public static boolean isJson(String value) { try { new JSONObject(value); } catch (JSONException e) { return false; } return true; } /** * 判斷是否是json結(jié)構(gòu) */ public static boolean isGoodJson(String json) { try { new JsonParser().parse(json); return true; } catch (JsonParseException e) { System.out.println("bad json: " + json); return false; } } ```
G.其他延申,補(bǔ)充說明
解決辦法:后臺(tái)輸出穩(wěn)定的Gson格式。此方法工程量太大
真正的問題是我的數(shù)據(jù)結(jié)構(gòu)有問題
例如下面Json字符串:
{"code":1,"info":"success","results":{"id":"1","name":"hehe"}}
results對(duì)應(yīng)的應(yīng)該是一個(gè)實(shí)體類,如果這個(gè)時(shí)候想把他解析為String或者List就會(huì)出現(xiàn)異常
如果參考使用GsonForm處理后的數(shù)據(jù)模型,幾乎不會(huì)出現(xiàn)問題;加入result后面的內(nèi)容可能在請(qǐng)求時(shí)會(huì)因?yàn)槟承┰驎?huì)存在格式上的變化,這個(gè)時(shí)候就有出現(xiàn)該異常的風(fēng)險(xiǎn)。Gson中,關(guān)鍵字后面出現(xiàn)""引起來的內(nèi)容將會(huì)被只認(rèn)為是STRING,“{}”只被認(rèn)為是類,“[]”只被認(rèn)為是List,這個(gè)幾乎是強(qiáng)制性的。
就是說如果你的實(shí)體預(yù)計(jì)是獲取String的變量,但是關(guān)鍵字后面對(duì)應(yīng)的卻出現(xiàn)了“{”或“[”,那么這個(gè)轉(zhuǎn)換將被認(rèn)為是錯(cuò)誤的,拋出異常。
3.4 android.content.ActivityNotFoundException: No Activity found to handle Intent
A.詳細(xì)崩潰日志信息
android.content.ActivityNotFoundException: No Activity found to handle Intent
B.查看崩潰類信息
當(dāng)調(diào)用{@link Context#startActivity}或其變體之一失敗時(shí),會(huì)引發(fā)此異常,因?yàn)闊o法找到執(zhí)行給定意圖的活動(dòng)。
public class ActivityNotFoundException extends RuntimeException { public ActivityNotFoundException() { } public ActivityNotFoundException(String name) { super(name); } };
C.項(xiàng)目中異常分析
D.引發(fā)崩潰日志的流程分析
F.解決辦法
第一種辦法:做一個(gè)try catch
Intent intent = new Intent(Intent.ACTION_SENDTO,url); try { context.startActivity(intent); } catch(ActivityNotFoundException exception) { Toast.makeText(this, "no activity", Toast.LENGTH_SHORT).show(); }
第二種辦法:判斷是否有應(yīng)用寶客戶端
//避免安裝了應(yīng)用寶的用戶點(diǎn)擊其他外部鏈接走此方法導(dǎo)致崩潰 //判斷是否用應(yīng)用寶客戶端 if(AppUtils.isPkgInstalled(AdDetailActivity.this,"com.tencent.android.qqdownloader")){ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); startActivity( intent); }3.5 Package manager has died導(dǎo)致崩潰
A.詳細(xì)崩潰日志信息
出錯(cuò)代碼位置 public static String softVersionName(Context context) { PackageInfo info = null; try { info = context.getPackageManager().getPackageInfo( context.getPackageName(), 0); //在這里 } catch (NameNotFoundException e) { e.printStackTrace(); } return info.versionName; }
B.查看崩潰類信息
C.項(xiàng)目中異常分析
D.引發(fā)崩潰日志的流程分析
原因分析(Binder造成)
如果一個(gè)進(jìn)程中使用的Binder內(nèi)容超過了1M,就會(huì)crash.
如果Binder的使用超出了一個(gè)進(jìn)程的限制就會(huì)拋出TransactionTooLargeException這個(gè)異常。
如果是其他原因造成Binder crash的話就會(huì)拋出RuntimeException。
F.解決辦法
public static String softVersionName(Context context) { PackageInfo info = null; try {//增加同步塊 synchronized (context) { info =context.getPackageManager().getPackageInfo(context.getPackageName(), 0); } return info.versionName; } catch (Exception e) { e.printStackTrace(); return ""; } }
G.其他延申
private void test() { //這個(gè)Demo就是同時(shí)創(chuàng)建兩個(gè)線程來進(jìn)行Binder調(diào)用. for (int i = 0; i < 2; i++) { new Thread() { @Override public void run() { int count = 0; Listlist = getPackageManager().getInstalledPackages(0); for (PackageInfo info : list) { if(count >=1000){ break; } try { PackageInfo pi = getPackageManager().getPackageInfo(info.packageName, PackageManager.GET_ACTIVITIES); } catch (PackageManager.NameNotFoundException e) { } } } }.start(); } } }
錯(cuò)誤打印日志
解決方式:其實(shí)只要避免多個(gè)線程同時(shí)來調(diào)用Binder就可以了,畢竟一個(gè)線程用了會(huì)釋放,所以理論上是很難發(fā)生的。
synchronized(MainActivity.class){ PackageInfo pi = getPackageManager() .getPackageInfo(info.packageName, PackageManager.GET_ACTIVITIES); }3.6 IllegalArgumentException View添加窗口錯(cuò)誤
A.詳細(xì)崩潰日志信息
View=com.android.internal.policy.impl.PhoneWindow$DecorView{22a4fb16 V.E..... R.....ID 0,0-1080,1020} not attached to window manager com.flyco.dialog.widget.base.BaseDialog.superDismiss(BaseDialog.java)
B.查看崩潰類信息
C.項(xiàng)目中異常分析
該異常表示view沒有添加到窗口管理器,通常是我們dismiss對(duì)話框的時(shí)候,activity已經(jīng)不存在了,建議不要在非UI線程操作對(duì)話框。
D.引發(fā)崩潰日志的流程分析
常發(fā)生這類Exception的情形都是,有一個(gè)費(fèi)時(shí)的線程操作,需要顯示一個(gè)Dialog,在任務(wù)開始的時(shí)候顯示一個(gè)對(duì)話框,然后當(dāng)任務(wù)完成了在Dismiss對(duì)話框,如果在此期間如果Activity因?yàn)槟撤N原因被殺掉且又重新啟動(dòng)了,那么當(dāng)dialog調(diào)用dismiss的時(shí)候WindowManager檢查發(fā)現(xiàn)Dialog所屬的Activity已經(jīng)不存在,所以會(huì)報(bào)錯(cuò)。要避免此類Exception,就要正確的使用對(duì)話框,也要正確的使用線程
F.解決辦法
可以參考崩潰bug日志總結(jié)1中的1.7
G.其他延申,建議
不要在非UI線程中使用對(duì)話框創(chuàng)建,顯示和取消對(duì)話框;
盡量少用多帶帶線程,出發(fā)是真正的耗時(shí)操作采用線程,線程也不要直接用Java式的匿名線程,除非是那種單純的操作,操作完成不需要做其他事情的。
如果是在fragment中發(fā)起異步網(wǎng)絡(luò)的回調(diào)中進(jìn)行dialog的操作,那么在操作之前,需要判斷 isAdd( ),避免fragment被回收了但是還要求dialog去dismiss
在Activity onDestroy中對(duì)Dialog提前進(jìn)行關(guān)閉
3.7 IllegalStateException: Not allowed to start service Intent異常崩潰
A.詳細(xì)崩潰日志信息
Caused by: java.lang.IllegalStateException: Not allowed to start service Intent { act=initApplication cmp=com.paidian.hwmc/.service.InitializeService }: app is in background uid UidRecord{a37d28d u0a386 TRNB bg:+5m30s482ms idle procs:3 seq(0,0,0)}
B.查看崩潰類信息
C.項(xiàng)目中異常分析
D.引發(fā)崩潰日志的流程分析
F.解決辦法
G.其他延申
3.8 java.lang.IllegalStateException:Can not perform this action after onSaveInstanceState
A.詳細(xì)崩潰日志信息
B.查看崩潰類信息
C.項(xiàng)目中異常分析
通過下面的源碼分析,我們可以知道,出現(xiàn)以上崩潰日志的原因,是因?yàn)槲覀冊(cè)诎聪马撁娣祷劓I的時(shí)候,當(dāng)前Activity以及在執(zhí)行銷毀操作(也就是說我們以前在其他地方調(diào)用了finish方法)。
D.引發(fā)崩潰日志的流程分析
問題所在是Activity#onBackPressed()方法。查看源代碼:點(diǎn)擊onBackPressed方法中的super
在FragmentActivity中
@Override public void onBackPressed() { if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) { super.onBackPressed(); } }
接著再次點(diǎn)擊super,在Activity中
public void onBackPressed() { if (mActionBar != null && mActionBar.collapseActionView()) { return; } if (!mFragments.getFragmentManager().popBackStackImmediate()) { finishAfterTransition(); } } public void finishAfterTransition() { if (!mActivityTransitionState.startExitBackTransition(this)) { finish(); } }
我們看到onBackPressed()方法執(zhí)行了兩個(gè)操作,第一個(gè)是獲取當(dāng)前的FragmentManager,并且執(zhí)行退棧操作,第二個(gè)是在退棧完成之后,執(zhí)行finish方法。繼續(xù)查看源碼,關(guān)鍵是FragmentManager實(shí)現(xiàn)類的popBackStackImmediate方法
@Override public boolean popBackStackImmediate() { checkStateLoss(); executePendingTransactions(); return popBackStackState(mHost.getHandler(), null, -1, 0); }
我們看到,在執(zhí)行退棧動(dòng)作之前,這里還有一步檢查操作
private void checkStateLoss() { if (mStateSaved) { throw new IllegalStateException( "Can not perform this action after onSaveInstanceState"); } if (mNoTransactionsBecause != null) { throw new IllegalStateException( "Can not perform this action inside of " + mNoTransactionsBecause); } }
從這里,我們終于找到了崩潰日志上的異常文案:Can not perform this action after onSaveInstanceState
F.解決辦法
方案1,在調(diào)用super.onBackPressed的時(shí)候,我們需要判斷當(dāng)前Activity是否正在執(zhí)行銷毀操作。
if (!isFinishing()) { super.onBackPressed(); }
方案2,通過上面的源碼分析,我們也知道了,super.onBackPressed最后也是調(diào)用finish()方法,因此我們可以重寫onBackPressed,直接調(diào)用finish方法。
G.其他延申
3.9 在Fragment中通過getActivity找不到上下文,報(bào)null導(dǎo)致空指針異常A.詳細(xì)崩潰日志信息
B.查看崩潰類信息
C.項(xiàng)目中異常分析
使用ViewPager+Fragment進(jìn)行視圖滑動(dòng),在某些部分邏輯也許我們需要利用上下文Context(例如基本的Toast),但是由于Fragment只是衣服在Activity容器的一個(gè)試圖,如果需要拿到當(dāng)前的Activity的上下文Context就必須通過getActivity()獲取。
遇過出現(xiàn)getActivity()出現(xiàn)null的時(shí)候?qū)е鲁绦驁?bào)出空指針異常。其實(shí)原因可以歸結(jié)于因?yàn)槲覀冊(cè)?/p>
切換fragment的時(shí)候,會(huì)頻繁被crash
系統(tǒng)內(nèi)存不足
橫豎屏幕切換的時(shí)候
以上情況都會(huì)導(dǎo)致Activity被系統(tǒng)回收,但是由于fragment的生命周期不會(huì)隨著Actiivty被回收而被回收,因此才會(huì)導(dǎo)致getActivity()出現(xiàn)null的問題。
很多人都曾被這個(gè)問題所困擾,如果app長(zhǎng)時(shí)間在后臺(tái)運(yùn)行,再次進(jìn)入app的時(shí)候可能會(huì)出現(xiàn)crash,而且fragment會(huì)有重疊現(xiàn)象。如果系統(tǒng)內(nèi)存不足、切換橫豎屏、app長(zhǎng)時(shí)間在后臺(tái)運(yùn)行,Activity都可能會(huì)被系統(tǒng)回收然后重建,但Fragment并不會(huì)隨著Activity的回收而被回收,創(chuàng)建的所有Fragment會(huì)被保存到Bundle里面,從而導(dǎo)致Fragment丟失對(duì)應(yīng)的Activity。
D.引發(fā)崩潰日志的流程分析
當(dāng)遇到getActivity()為null,或getContext()時(shí),先冷靜想想以下3點(diǎn):
1.是不是放在了第三方的回調(diào)中
2.是不是在其他進(jìn)程中調(diào)用了(其實(shí)第一點(diǎn)就是在其他進(jìn)程中調(diào)用了)
3.是不是調(diào)用時(shí)不在指定生命周期范圍內(nèi)(onAttach與onDetach之間)
F.解決辦法
在Fragment中直接調(diào)用 private MActivity mActivity; @Override public void onAttach(Activity activity) { super.onAttach(activity); mActivity = (MActivity) activity; } @Override public void onDetach() { super.onDetach(); mActivity = null; }
G.其他延申
源碼解讀:在FragmentActivity中
@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); Parcelable p = mFragments.saveAllState(); if (p != null) { outState.putParcelable(FRAGMENTS_TAG, p); } …… }
如果從最近使用的應(yīng)用里面點(diǎn)擊我們的應(yīng)用,系統(tǒng)會(huì)恢復(fù)之前被回收的Activity,這個(gè)時(shí)候FragmentActivity在oncreate里面也會(huì)做Fragment的恢復(fù)
@SuppressWarnings("deprecation") @Override protected void onCreate(@Nullable Bundle savedInstanceState) { mFragments.attachHost(null /*parent*/); super.onCreate(savedInstanceState); NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance(); if (nc != null) { mFragments.restoreLoaderNonConfig(nc.loaders); } if (savedInstanceState != null) { Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG); mFragments.restoreAllState(p, nc != null ? nc.fragments : null); …… } if (mPendingFragmentActivityResults == null) { mPendingFragmentActivityResults = new SparseArrayCompat<>(); mNextCandidateRequestIndex = 0; } mFragments.dispatchCreate(); }
假設(shè)我們的頁面叫MyActivity(繼承自FragmentActivity),其中用到的Fragment叫MyFragment。出現(xiàn)上面這種情況時(shí),app發(fā)生的變化如下:
1、在前面提到的幾種情況下系統(tǒng)回收了MyActivity
2、通過onSaveInstanceState保存MyFragment的狀態(tài)
3、用戶再次點(diǎn)擊進(jìn)入app
4、由于MyActivity被回收,系統(tǒng)會(huì)重啟MyActivity,根據(jù)之前保存的MyFragment的狀態(tài)恢復(fù)fragment
5、MyActivity的代碼邏輯中,會(huì)再次創(chuàng)建新的MyFragment
6、頁面出現(xiàn)混亂,覆蓋了兩層的fragment。假如恢復(fù)的MyFragment使用到了getActivity()方法,會(huì)報(bào)空指針異常
對(duì)于上面的問題,可以考慮下面這兩種解決辦法:
1、不保存fragment的狀態(tài):在MyActivity中重寫onSaveInstanceState方法,將super.onSaveInstanceState(outState);注釋掉,讓其不再保存Fragment的狀態(tài),達(dá)到fragment隨MyActivity一起銷毀的目的。
2、重建時(shí)清除已經(jīng)保存的fragment的狀態(tài):在恢復(fù)Fragment之前把Bundle里面的fragment狀態(tài)數(shù)據(jù)給清除。方法如下:
if(savedInstanceState!= null){ String FRAGMENTS_TAG = "Android:support:fragments"; savedInstanceState.remove(FRAGMENTS_TAG); }4.1 IllegalArgumentException導(dǎo)致崩潰【url地址傳入非法參數(shù),轉(zhuǎn)義字符】
A.詳細(xì)崩潰日志信息
B.查看崩潰類信息
C.項(xiàng)目中異常分析
只有很少一部分傳入非法參數(shù)導(dǎo)致崩潰,不能直接用常規(guī)方法。需要過濾
D.引發(fā)崩潰日志的流程分析
F.解決辦法
Java調(diào)用 URLDecoder.decode(str,"UTF-8");拋出以上的異常,其主要原因是%在URL中是特殊字符,需要特殊轉(zhuǎn)義一下
public static String replacer(String data) { try { //使用%25替換字符串中的%號(hào) data = data.replaceAll("%(?![0-9a-fA-F]{2})", "%25"); data = URLDecoder.decode(data, "utf-8"); } catch (Exception e) { e.printStackTrace(); } return data; }4.2 ClassNotFoundException: Didn"t find class "" on path: /data/app/*錯(cuò)誤
A.詳細(xì)崩潰日志信息
java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{*****Activity}: java.lang.ClassNotFoundException: Didn"t find class "*****Activity" on path: /data/app/*******.apk
B.查看崩潰類信息
當(dāng)應(yīng)用程序嘗試使用字符串名稱加載類時(shí)引發(fā):但無法找到具有指定名稱的類的定義。從1.4版開始,已對(duì)此異常進(jìn)行了修改,以符合通用的異常鏈接機(jī)制。在構(gòu)建時(shí)提供并通過{@link#getException()}方法訪問的“在加載類時(shí)引發(fā)的可選異?!爆F(xiàn)在稱為原因,并且可以通過{@link Throwable#getCace()}方法以及前面提到的“遺留方法”進(jìn)行訪問。
public class ClassNotFoundException extends ReflectiveOperationException { private static final long serialVersionUID = 9176873029745254542L; private Throwable ex; public ClassNotFoundException() { super((Throwable)null); // Disallow initCause } public ClassNotFoundException(String s) { super(s, null); // Disallow initCause } public ClassNotFoundException(String s, Throwable ex) { super(s, null); // Disallow initCause this.ex = ex; } public Throwable getException() { return ex; } public Throwable getCause() { return ex; } }
C.項(xiàng)目中異常分析
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/75919.html
摘要:以前一直想寫一篇總結(jié)開發(fā)經(jīng)驗(yàn)的文章,估計(jì)當(dāng)時(shí)的我還達(dá)不到某種水平,所以思路跟不上,下筆又捉襟見肘。在需求都還沒完成的時(shí)候把大量時(shí)間花在優(yōu)化上是本末倒置的優(yōu)化要用實(shí)際數(shù)據(jù)說話,借助測(cè)試工具進(jìn)行檢測(cè)如網(wǎng)易的騰訊的和,科大訊飛的,的。 以前一直想寫一篇總結(jié) Android 開發(fā)經(jīng)驗(yàn)的文章,估計(jì)當(dāng)時(shí)的我還達(dá)不到某種水平,所以思路跟不上,下筆又捉襟見肘。近日,思路較為明朗,于是重新操起鍵盤開始碼...
摘要:導(dǎo)語智能手機(jī)發(fā)展到今天已經(jīng)有十幾個(gè)年頭,手機(jī)的軟硬件都已經(jīng)發(fā)生了翻天覆地的變化,特別是陣營(yíng),從一開始的一兩百到今天動(dòng)輒,內(nèi)存。恰好最近做了內(nèi)存優(yōu)化相關(guān)的工作,這里也對(duì)內(nèi)存優(yōu)化相關(guān)的知識(shí)做下總結(jié)。 導(dǎo)語 智能手機(jī)發(fā)展到今天已經(jīng)有十幾個(gè)年頭,手機(jī)的軟硬件都已經(jīng)發(fā)生了翻天覆地的變化,特別是Android陣營(yíng),從一開始的一兩百M(fèi)到今天動(dòng)輒4G,6G內(nèi)存。然而大部分的開發(fā)者觀看下自己的異常上報(bào)系...
閱讀 2979·2021-11-23 10:12
閱讀 2702·2021-11-23 09:51
閱讀 2050·2021-11-15 11:37
閱讀 1390·2019-08-30 15:55
閱讀 1974·2019-08-29 15:40
閱讀 1176·2019-08-28 18:30
閱讀 1656·2019-08-28 18:02
閱讀 2652·2019-08-26 12:00