国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Android實際開發bug大總結

peixn / 4323人閱讀

摘要:換句話說,環境或應用程序沒有處于請求操作的適當狀態。項目中異常分析引發崩潰日志的流程分析解決辦法常見的出現場景狀態異常非法線程操作。導致的方法出來顯示消息位于該消息之后,遲遲沒有執行。這時候,的超時檢測結束,刪除了服務中的記錄。

目錄介紹

1.1 java.lang.UnsatisfiedLinkError找不到so庫異常

1.2 java.lang.IllegalStateException非法狀態異常

1.3 android.content.res.Resources$NotFoundException

1.4 java.lang.IllegalArgumentException參數不匹配異常

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類轉化異常

1.9 Toast運行在子線程問題,handler問題

2.1 java.lang.ClassNotFoundException類找不到異常

2.2 java.util.concurrent.TimeoutException連接超時崩潰

2.3 java.lang.NumberFormatException格式轉化錯誤

2.4 java.lang.IllegalStateException: Fragment not attached to Activity

2.5 ArrayIndexOutOfBoundsException 角標越界異常

2.6 IllegalAccessException 方法中構造方法權限異常

2.7 android.view.WindowManager$BadTokenException,dialog彈窗異常

2.8 java.lang.NoClassDefFoundError 找不到類異常

2.9 Android出現: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導致崩潰

3.6 IllegalArgumentException View添加窗口錯誤

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找不到上下文,報null導致空指針異常

4.1 IllegalArgumentException導致崩潰【url地址傳入非法參數,轉義字符】

4.2 ClassNotFoundException: Didn"t find class "" on path: /data/app/*錯誤

4.3 NoClassDefFoundError異常【該異常表示找不到類定義】

4.4 公司之前項目使用客服udesk,sdk更新后初始化導致崩潰問題

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庫導致崩潰

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基礎及深入知識點,Android技術博客,Python學習筆記等等,還包括平時開發中遇到的bug匯總,當然也在工作之余收集了大量的面試題,長期更新維護并且修正,持續完善……開源的文件是markdown格式的!同時也開源了生活博客,從12年起,積累共計47篇[近20萬字],轉載請注明出處,謝謝!

鏈接地址:https://github.com/yangchong2...

如果覺得好,可以star一下,謝謝!當然也歡迎提出建議,萬事起于忽微,量變引起質變!

1.1 java.lang.UnsatisfiedLinkError

A.詳細崩潰日志信息

# 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.查看崩潰類信息

這個異常類的大意是:如果Java虛擬機找不到聲明為本機的方法的適當本機語言定義,則引發。

public class UnsatisfiedLinkError extends LinkageError {
    private static final long serialVersionUID = -4019343241616879428L;

    public UnsatisfiedLinkError() {
        super();
    }

    public UnsatisfiedLinkError(String s) {
        super(s);
    }
}

C.項目中異常分析

根據實際項目可知,當準備播放視頻時,找不到libijkffmpeg.so這個庫,導致直接崩潰。

D.引發崩潰日志的流程分析

F.解決辦法

報這個錯誤通常是so庫加載失敗,或者找不到準備執行的JNI方法:

1.建議檢查so在安裝的過程中是否丟失,沒有放入指定的目錄下;

2.調用loadLibrary時檢查是否調用了正確的so文件名,并對其進行捕獲,進行相應的處理,防止程序發生崩潰;

3.檢查下so的架構是否跟設備架構一至(如在64-bit架構下調用32-bit的so)。

代碼展示

ndk {
    //根據需要 自行選擇添加的對應cpu類型的.so庫。
    //abiFilters "armeabi", "armeabi-v7a", "arm64-v8a", "x86", "mips"
    abiFilters "armeabi-v7a"
}

dependencies {
    compile fileTree(dir: "libs", include: ["*.jar"])
    //這兩個是必須要加的,其它的可供選擇
    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.知識延申

Android 應用開發者應該對 UnsatisfiedLinkError 這種類型的錯誤比較熟悉了,這個問題一直困擾著廣大的開發者,那么有沒有想過有可能你什么都沒做錯,也會出現這個問題呢?

我們在 Android 應用開發測試過程中曾經碰到過這樣的案例,apk 在某機型上安裝完成之后運行即崩潰,報錯 UnsatisfiedLinkError。

java.lang.UnsatisfiedLinkError: Couldn’t load mobsec from loader dalvik.system.PathClassLoader.....findLibrary returned null

首先懷疑是在 apk 中相應的 libsabi 目錄下沒有放置 libmobsec.so,然而檢查發現這個 so 在所有的 libsabi 下都有放置過,繼續排查;

然后的想法是放置的 so 不是對應 abi 的,比如由于粗心在 armeabi 目錄下放置了 x86 指令集的 so,導致在 armeabi 指令集手機上加載出錯,這個也被排除掉;

就在沒有頭緒的時候,想到 System.loadLibrary 函數加載 so 時,系統是從指定的路徑下加載的,那么這個路徑下 so 是否存在呢?

我們知道應用的私有 Native library 目錄 /data/data/packagename/lib 是一個符號鏈接,鏈接到 /data/app-lib/ 目錄,System.loadLibrary 是到這個目錄去嘗試加載 so 的。

adb shell 到這個路徑下,使用命令 ls 查看,果然這個 libmobsec.so 是不存在的。那么是什么原因導致的呢?

分析 Android 系統源碼的實現,發現 /data/app-lib/ 這個目錄下的 so ,是在系統安裝 apk 時從 apk 的 lib 目錄下去抽取的。

1.2 java.lang.IllegalStateException非法狀態異常

A.詳細崩潰日志信息

onSaveInstanceState方法是在該Activity即將被銷毀前調用,來保存Activity數據的,如果在保存玩狀態后

再給它添加Fragment就會出錯。

IllegalStateException: Can not perform this action after onSaveInstanceState:

B.查看崩潰類信息

在非法或不適當的時間調用方法的信號。換句話說,Java環境或Java應用程序沒有處于請求操作的適當狀態。

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.項目中異常分析

分析

D.引發崩潰日志的流程分析

F.解決辦法

解決辦法就是把commit()方法替換成 commitAllowingStateLoss()

G.其他延申

錯誤類型大致為以下幾種:

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());而這里是因為兩次toString()結果不同,導致不同的tag指向的是同一個fragment。獲取fragment的tag的正確方法應該是使用其提供的fragment.getTag()方法。

第二種:該異常是由于服務器錯誤返回的JSON字符串和服務器正常下時返回的JSON字符串結構不同,導致利用Gson解析的時候報了一個異常:本該去解析集合卻強制去解析對象所致.解決辦法:在使用Gson解析JSON時try cash一下,不報錯按照正常邏輯繼續解析,報異常則處理為請求失敗邏輯即可.

1.3 android.content.res.Resources$NotFoundException

A.詳細崩潰日志信息

Android資源不是可繪制的(顏色或路徑)

Resource is not a Drawable (color or path): TypedValue{t=0x2/d=0x7f040151 a=2}
android.view.LayoutInflater.createView(LayoutInflater.java:620)

B.查看崩潰類信息

當找不到請求的資源時,資源API將引發此異常。

public static class NotFoundException extends RuntimeException {
    public NotFoundException() {
    }

    public NotFoundException(String name) {
        super(name);
    }

    public NotFoundException(String name, Exception cause) {
        super(name, cause);
    }
}

C.項目中異常分析

由于將圖片資源拷貝到了drawable-land-xhdpi目錄下,本來應該拷貝到drawable-xhdpi目錄下。

D.引發崩潰日志的流程分析

F.解決辦法

1.引用的資源ID 是否能匹配到R.java文件中定義的資源;

2.是否因為緩存等原因導致編譯APK時未把資源文件打包進去,可以把APK反編譯檢查下;

3.是否使用了一個錯誤的類型來引用了某個資源或者配置資源時存在錯誤;

4.是否將Int等整型變量作為了參數傳給了View.setText調用,這種情況下該整型變量將被認為是一個資源ID號去資源列表中查找對應的資源,導致找不到對應資源錯誤;解決方法是做類型轉換View.setText(String.valueOf(Int id))。

1.4 java.lang.IllegalArgumentException參數不匹配異常

A.詳細崩潰日志信息

B.查看崩潰類信息

參數不匹配異常,通常由于傳遞了不正確的參數導致。

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.項目中異常分析

D.引發崩潰日志的流程分析

F.解決辦法

G.常見的出現場景

Activity、Service狀態異常;

非法URL;

UI線程操作。

Fragment中嵌套了子Fragment,Fragment被銷毀,而內部Fragment未被銷毀,所以導致再次加載時重復,在onDestroyView() 中將內部Fragment銷毀即可

在請求網絡的回調中使用了glide.into(view),view已經被銷毀會導致該錯誤

1.5 IllegalStateException:Can"t compress a recycled bitmap

A.詳細崩潰日志信息

無法壓縮回收位圖

Can"t compress a recycled bitmap
com.paidian.hwmc.utils.i.a(FileUtils.java:75)

B.查看崩潰類信息

如果位圖已被回收,則希望拋出異常的方法將調用此值。知道了崩潰的具體位置,就該分析具體的原因呢!

public boolean compress(CompressFormat format, int quality, OutputStream stream) {
    checkRecycled("Can"t compress a recycled bitmap");
    //省略代碼
    return result;
}

//如果位圖已被回收,則希望拋出異常的方法將調用此值。
private void checkRecycled(String errorMessage) {
    if (mRecycled) {
        throw new IllegalStateException(errorMessage);
    }
}

C.項目中異常分析

使用了已經被釋放過內存的對象。對于Bitmap:Bitmap bitmap=一個bitmap對象。使用過程中調用bitmap.recycle(),之后再使用bitmap就會報錯。

D.引發崩潰日志的流程分析

bitmap.recycle()解釋如下所示,釋放與此位圖關聯的本機對象,并清除對像素數據的引用。這將不會同步釋放像素數據;它只允許在沒有其他引用的情況下對其進行垃圾收集。位圖被標記為“死”,這意味著如果調用getPixels()或setPixels(),它將拋出異常,而不會繪制任何內容。此操作不能反轉,因此只有在確定沒有進一步使用位圖的情況下才應調用該操作。這是一個高級調用,通常不需要調用,因為當沒有對此位圖的引用時,普通GC進程將釋放此內存。

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.詳細崩潰日志信息

Please call the AutoSizeConfig#init() first
com.paidian.hwmc.base.BaseApplication.initAutoSizeConfig(BaseApplication.java:386)

B.查看崩潰類信息

空指針異常,也是十分常見的一個異常

public class NullPointerException extends RuntimeException {
    private static final long serialVersionUID = 5162710183389028792L;
    public NullPointerException() {
        super();
    }
    public NullPointerException(String s) {
        super(s);
    }
}

C.項目中異常分析

空指針發生場景較多,是指某一個對象報null,這個使用去使用它的話就i會報該異常。

D.引發崩潰日志的流程分析

導致出現空指針的原因: 必須滿足兩個條件才會發生空指針:引用變量指向了空,并且調用了這個引用的方法

空指針問題解決思路:

查看Log信息看第一行導致空指針發生的代碼,直接雙擊打開報空指針的類

查看該行代碼中有幾處調用了方法,則有幾個對象可能是空的,找出哪個對象是空的

查看這些對方在哪里賦值了

如果沒賦值,則給她賦值,問題解決

如果有地方賦值了,則看這個方法有沒有被調用(Ctrl + Shift + G)

如果沒有調用(可能沒調用或可能調用時機太晚),在使用她前先調用賦值,問題解決

如果有調用,則看是不是有其它地方又給她賦值為null了,如果沒有設置為null,則要看賦值的變量和我們使用時的變量是否是同一個變量。

F.解決辦法

空指針最為常見,也最容易規避,使用的時候一定要進行null check,采取不信任原則:

1.方法形參要判空后才使用;

2.全局變量容易被系統回收或者更改,使用全局變量前建議判空;

3.第三方接口的調用,對返回值進行判空。

4.請注意線程安全

1.7 android.view.WindowManager$BadTokenException異常,Toast報錯Unable to add window

A.詳細崩潰日志信息

報錯日志,是不是有點眼熟呀?更多可以看我的開源項目: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.查看崩潰類信息

查詢報錯日志是從哪里來的

C.項目中異常分析

D.引發崩潰日志的流程分析

這個異常發生在Toast顯示的時候,原因是因為token失效。通常情況下,一般是不會出現這種異常。但是由于在某些情況下, Android進程某個UI線程的某個消息阻塞。導致 TN 的 show 方法 post 出來 0 (顯示) 消息位于該消息之后,遲遲沒有執行。這時候,NotificationManager 的超時檢測結束,刪除了 WMS 服務中的 token 記錄。刪除 token 發生在 Android 進程 show 方法之前。這就導致了上面的異常。

測試代碼。模擬一下異常的發生場景,其實很容易,只需要這樣做就可以出現上面這個問題

 Toast.makeText(this,"瀟湘劍雨-yc",Toast.LENGTH_SHORT).show();
    try {
        Thread.sleep(20000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

F.解決辦法

目前見過好幾種,思考一下那種比較好……

第一種,既然是報is your activity running,那可以不可以在吐司之前先判斷一下activity是否running呢?

第二種,拋出異常增加try-catch,代碼如下所示,最后仍然無法解決問題

按照源碼分析,異常是發生在下一個UI線程消息中,因此在上一個ui線程消息中加入try-catch是沒有意義的。而且用到吐司地方這么多,這樣做也不方便啦!

第三種,那就是自定義類似吐司Toast的view控件。個人建議除非要求非常高,不然不要這樣做。畢竟發生這種異常還是比較少見的

G.哪些情況會發生該問題?

UI 線程執行了一條非常耗時的操作,比如加載圖片等等,就類似上面用 sleep 模擬情況

進程退后臺或者息屏了,系統為了減少電量或者某種原因,分配給進程的cpu時間減少,導致進程內的指令并不能被及時執行,這樣一樣會導致進程看起來”卡頓”的現象

當TN拋出消息的時候,前面有大量的 UI 線程消息等待執行,而每個 UI 線程消息雖然并不卡頓,但是總和如果超過了 NotificationManager 的超時時間,還是會出現問題

1.8 java.lang.ClassCastException類轉化異常

A.詳細崩潰日志信息

android.widget.FrameLayout cannot be cast to android.widget.RelativeLayout
com.paidian.hwmc.goods.activity.GoodsDetailsActivity.initView(GoodsDetailsActivity.java:712)

B.查看崩潰類信息

拋出以指示代碼試圖將對象強制轉換為它不是實例的子類。

public class ClassCastException extends RuntimeException {
    private static final long serialVersionUID = -9223365651070458532L;


    public ClassCastException() {
        super();
    }

    public ClassCastException(String s) {
        super(s);
    }
}

C.項目中異常分析

該異常表示類型轉換異常,通常是因為一個類對象轉換為其他不兼容類對象拋出的異常,檢查你要轉換的類對象類型。

D.引發崩潰日志的流程分析

F.解決辦法

一般在強制類型轉換時出現,例如如果A向B轉換,而A不是B的父類時,將產生java.lang.ClassCastException異常。一般建議做這時要使用instanceof做一下類型判斷,再做轉換。

該案例中,需要把FrameLayout更改成RelativeLayout就可以呢

1.9 Toast運行在子線程問題,handler問題

A.詳細崩潰日志信息

先來看看問題代碼,會出現什么問題呢?

new Thread(new Runnable() {
    @Override
    public void run() {
        ToastUtils.showRoundRectToast("瀟湘劍雨-楊充");
    }
}).start();

報錯日志如下所示:

然后找找報錯日志從哪里來的

![image]()

子線程中吐司的正確做法,代碼如下所示

new Thread(new Runnable() {
    @Override
    public void run() {
        Looper.prepare();
        ToastUtils.showRoundRectToast("瀟湘劍雨-楊充");
        Looper.loop();
    }
}).start();

得出的結論

Toast也可以在子線程執行,不過需要手動提供Looper環境的。

Toast在調用show方法顯示的時候,內部實現是通過Handler執行的,因此自然是不阻塞Binder線程,另外,如果addView的線程不是Loop線程,執行完就結束了,當然就沒機會執行后續的請求,這個是由Hanlder的構造函數保證的。可以看看handler的構造函數,如果Looper==null就會報錯,而Toast對象在實例化的時候,也會為自己實例化一個Hanlder,這就是為什么說“一定要在主線程”,其實準確的說應該是 “一定要在Looper非空的線程”。

Handler的構造函數如下所示:

2.1 java.lang.ClassNotFoundException類找不到異常

A.詳細崩潰日志信息

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.查看崩潰類信息

當應用程序嘗試使用字符串名稱加載類時引發:但無法找到具有指定名稱的類的定義。從1.4版開始,已對此異常進行了修改,以符合通用的異常鏈接機制。在構建時提供并通過{@link#getException()}方法訪問的“在加載類時引發的可選異常”現在稱為原因,并且可以通過{@link Throwable#getCace()}方法以及前面提到的“遺留方法”進行訪問。

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.項目中異常分析

該異常表示在路徑下,找不到指定類,通常是因為構建路徑問題導致的。

D.引發崩潰日志的流程分析

F.解決辦法

類名是以字符串形式標識的,可信度比較低,在調用Class.forName(""),Class.findSystemClass(""),Class.loadClass("")等方法時,找不到類名時將會報錯。如果找不到的Class是系統Class,那么可能是系統版本兼容,廠家Rom兼容的問題,找到對應的設備嘗試重現,解決方法可以考慮更換Api,或用自己實現的Class替代。

如果找不到的Class是應用自由Class(含第三方SDK的Class),可以通過反編譯工具查看對應apk中是否真的缺少該Class,再進行定位,這種往往發生在:

1.要找的Class被混淆了,存在但名字變了;

2.要找的Class未被打入Dex,確實不存在,可能是因為自己的疏忽,或編譯環境的沖突;

3.要找的Class確實存在,但你的Classlorder找不到這個Class,往往因為這個Classloder是你自實現的(插件化應用中常見)。

G.其他延申

2.2 java.util.concurrent.TimeoutException連接超時崩潰

A.詳細崩潰日志信息

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.查看崩潰類信息

當阻塞操作超時引發的異常。指定超時的阻塞操作需要一種方法來指示已發生超時。對于許多此類操作,可以返回指示超時的值;如果不可能或不需要,則應聲明并拋出{@code TimeoutException}。

public class TimeoutException extends Exception {
    private static final long serialVersionUID = 1900926677490660714L;
    public TimeoutException() {}
    public TimeoutException(String message) {
        super(message);
    }
}

C.項目中異常分析

D.引發崩潰日志的流程分析

F.解決辦法

一般是系統在gc時,調用對象的finalize超時導致,解決辦法:

1.檢查分析finalize的實現為什么耗時較高,修復它;

2.檢查日志查看GC是否過于頻繁,導致超時,減少內容開銷,防止內存泄露。

G.其他延申

2.3 java.lang.NumberFormatException格式轉化錯誤

A.詳細崩潰日志信息

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.查看崩潰類信息

引發,以指示應用程序試圖將字符串轉換為數字類型之一,但該字符串沒有適當的格式。

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.項目中異常分析

錯誤關鍵字 java.lang.NumberFormatException 這句話明確告訴了我們是數字格式異常,接著后面有 For input string: "100 " 提示,這就告訴我們,當前想把 "100 " 轉換成數字類型時出錯了,這樣就很確切了。

D.引發崩潰日志的流程分析

F.解決辦法

解決辦法很簡單,改成 Integer.parseInt(str.trim()),注意將字符串轉化成整數數據類型時,注意需要trim一下。

G.其他延申

2.4 java.lang.IllegalStateException: Fragment not attached to Activity

A.詳細崩潰日志信息

java.lang.IllegalStateException: Fragment not attached to Activity

B.查看崩潰類信息

C.項目中異常分析

出現該異常,是因為Fragment的還沒有Attach到Activity時,調用了如getResource()等,需要上下文Content的函數。

出現該異常,是因為Fragment還沒有Attach到Activity時,調用了如getResource()等,需要上下文Context的函數。解決方法,就是等將調用的代碼寫在OnStart()中。

D.引發崩潰日志的流程分析

F.解決辦法

將調用的代碼運行在Fragment Attached的生命周期內。

第一種:在調用需要Context的函數之前,增加一個判斷isAdded()

if(isAdded()){//isAdded方法是Android系統提供的,只有在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.其他延申

發生場景:該錯誤經常發生在fragment的線程中執行了一個耗時操作,線程在執行完畢后會調用getResources來更新ui。如果在線程操作沒有完成,就調用getActivity().recreate()重新加載activity或屏幕旋轉,這時就會出現Fragment not attached to Activity的錯誤

2.5 ArrayIndexOutOfBoundsException 角標越界異常

A.詳細崩潰日志信息

該異常表示數組越界

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.查看崩潰類信息

引發,以指示已使用非法索引訪問數組。索引不是負的,就是大于或等于數組的大小。

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.項目中異常分析

D.引發崩潰日志的流程分析

F.解決辦法

這種情況一般要在數組循環前做好length判斷,index超出length上限和下限時都會報錯。舉例如下:一個數組int test[N],一共有N個元素分別是test[0]~test[N-1],如果調用test[N],將會報錯。建議讀取時,不要超過數組的長度(array.length)。

Android中一種常見情形就是上拉刷新中header也會作為listview的第0個位置,如果判斷失誤很容易造成越界。

G.其他延申

2.6 IllegalAccessException 方法中構造方法權限異常

A.詳細崩潰日志信息

Unable to instantiate application com.pedaily.yc.meblurry.App: java.lang.IllegalAccessException

B.查看崩潰類信息

當應用程序試圖反射地創建實例(數組除外)、設置或獲取字段或調用方法時,將引發IllegalAccessException,但當前執行的方法無法訪問指定的類、字段、方法或構造函數的定義。

public class IllegalAccessException extends ReflectiveOperationException {
    private static final long serialVersionUID = 6616958222490762034L;
    public IllegalAccessException() {
        super();
    }
    public IllegalAccessException(String s) {
        super(s);
    }
}

C.項目中異常分析

錯誤提示是,構造方法的權限不對

D.引發崩潰日志的流程分析

F.解決辦法

檢查了整個Application,才發現,原來有一個無參數的構造方法,被設計成private。修改其為public即可。

G.其他延申

android BroadcastReceiver遇到java.lang.IllegalAccessException解決方法,錯誤原因主要是app中其他地方調用了默認的構造函數,必須增加默認構造函數且訪問權限為public

2.7 android.view.WindowManager$BadTokenException,dialog彈窗異常

A.詳細崩潰日志信息

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中可以找到這個異常類,主要發生在嘗試添加視圖時引發的

public static class BadTokenException extends RuntimeException {
    public BadTokenException() {
    }

    public BadTokenException(String name) {
        super(name);
    }
}

C.項目中異常分析

該異常表示不能添加窗口,通常是所要依附的view已經不存在導致的。

D.引發崩潰日志的流程分析

F.解決辦法

之前項目中有一個自定義彈窗,偶爾會報這個錯。解決辦法如下代碼所示

主要邏輯是在彈窗show或者dismiss的時候,都增加了邏輯判斷,判斷宿主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不能正確使用時,經常會報出該異常,原因比較多,幾個常見的場景如下:

1.上一個頁面沒有destroy的時候,之前的Activity已經接收到了廣播。如果此時之前的Activity進行UI層面的操作處理,就會造成crash。UI層面的刷新,一定要注意時機,建議使用set_result來代替廣播的形式進行刷新操作,避免使用廣播的方式,代碼不直觀且容易出錯。

2.Dialog在Actitivty退出后彈出。在Dialog調用show方法進行顯示時,必須要有一個Activity作為窗口的載體,如果Activity被銷毀,那么導致Dialog的窗口載體找不到。建議在Dialog調用show方法之前先判斷Activity是否已經被銷毀。

3.Service&Application彈出對話框或WindowManager添加view時,沒有設置window type為TYPE_SYSTEM_ALERT。需要在調用dialog.show()方法前添加dialog.getWindow().SetType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)。

4.6.0的系統上, (非定制 rom 行為)若沒有給予懸浮窗權限, 會彈出該問題, 可以通過Settings.canDrawOverlays來判斷是否有該權限.

5.某些不穩定的MIUI系統bug引起的權限問題,系統把Toast也當成了系統級彈窗,android6.0的系統Dialog彈窗需要用戶手動授權,若果app沒有加入SYSTEM_ALERT_WINDOW權限就會報這個錯。需要加入給app加系統Dialog彈窗權限,并動態申請權限,不滿足第一條會出現沒權限閃退,不滿足第二條會出現沒有Toast的情況。

H.其他建議

1.不要在非UI線程中使用對話框創建,顯示和取消對話框;

2.盡量少用多帶帶線程,出發是真正的耗時操作采用線程,線程也不要直接用Java式的匿名線程,除非是那種單純的操作,操作完成不需要做其他事情的。

3.如果是在fragment中發起異步網絡的回調中進行dialog的操作,那么在操作之前,需要判斷 isAdd( ),避免fragment被回收了但是還要求dialog去dismiss

4.在Activity onDestroy中對Dialog提前進行關閉

2.8 java.lang.NoClassDefFoundError 找不到類異常

A.詳細崩潰日志信息

B.查看崩潰類信息

如果Java虛擬機或ClassLoader實例試圖加載類的定義(作為普通方法調用的一部分或使用新的表達式創建新實例的一部分),則拋出該類的定義。編譯當前執行的類時存在搜索類定義,但無法再找到該定義。

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.項目中異常分析

問題的主要原因:方法數超65536限制。由于實際開發當中的需求不斷變更,開源框架越來越多,大多都用第三方SDK,導致方法數很容易超出65536限制。出現錯誤Java.lang.NoClassDefFoundError

D.引發崩潰日志的流程分析

這個錯誤是Android應用的方法總數限制造成的。android平臺的Java虛擬機Dalvik在執行DEX格式的Java應用程序時,使用原生類型short來索引DEX文件中的方法。這意味著單個DEX文件可被引用的方法總數被限制為65536。通常APK包含一個classes.dex文件,因此Android應用的方法總數不能超過這個數量,這包括Android框架、類庫和你自己開發的代碼。而Android 5.0和更高版本使用名為ART的運行時,它原生支持從APK文件加載多個DEX文件。在應用安裝時,它會執行預編譯,掃描classes(..N).dex文件然后將其編譯成單個.oat文件用于執行. 通熟的講,就是分包。

F.解決辦法

64k解決辦法

G.其他延申

該異常表示找不到類定義,當JVM或者ClassLoader實例嘗試裝載該類的定義(這通常是一個方法調用或者new表達式創建一個實例過程的一部分)而這個類定義并沒有找時所拋出的錯誤。

[解決方案]:NoClassDefFoundError異常一般出現在編譯環境和運行環境不一致的情況下,就是說有可能在編譯過后更改了Classpath或者jar包所以導致在運行的過程中JVM或者ClassLoader無法找到這個類的定義。

1.分dex包編程,如果依賴的dex包刪除了指定的類,執行初始化方法時將會報錯;

2.使用第三方SDK或插件化編程時,動態加載或實例化類失敗將會報錯;

3.系統資源緊張時,當大量class需要加載到內存的時候,處于競爭關系,部分calss競爭失敗,導致加載不成功;

4.裝載并初始化一個類時失敗(比如靜態塊拋 java.lang.ExceptionInInitializerError 異常),然后再次引用此類也會提示NoClassDefFoundErr 錯誤;

5.手機系統版本或硬件設備不匹配(如ble設備只支持18以上SDK),程序引用的class在低版本中不存在,導致NoClassDefFoundErr 錯誤。

6.so文件找不到,設備平臺armeabi-v7a,但是我的so庫是放在armeabi中的,解決方法新建一個armeabi-v7a包,并且把armeabi的文件拷貝過來.

2.9 Android出現:Your project path contains non-ASCII characters.

A.詳細崩潰日志信息

B.查看崩潰類信息

C.項目中異常分析

D.引發崩潰日志的流程分析

F.解決辦法

很好解決啦,就是你的工程項目路徑或者項目名稱包含了中文,修改相關的名稱就好

G.其他延申

3.1 OnErrorNotImplementedException【 Can"t create handler inside thread that has not called Looper.prepare()】

A.詳細崩潰日志信息

Can"t create handler inside thread that has not called Looper.prepare()

B.查看崩潰類信息

C.項目中異常分析

D.引發崩潰日志的流程分析

這是因為Handler對象與其調用者在同一線程中,如果在Handler中設置了延時操作,則調用線程也會堵塞。每個Handler對象都會綁定一個Looper對象,每個Looper對象對應一個消息隊列(MessageQueue)。如果在創建Handler時不指定與其綁定的Looper對象,系統默認會將當前線程的Looper綁定到該Handler上。

在主線程中,可以直接使用new Handler()創建Handler對象,其將自動與主線程的Looper對象綁定;在非主線程中直接這樣創建Handler則會報錯,因為Android系統默認情況下非主線程中沒有開啟Looper,而Handler對象必須綁定Looper對象。

如果在主線程中創建handler時,系統會自動創建Looper,但是在子線程中創建handler時,是不會自動創建Looper的,此時如果不手動創建Looper,系統就會崩潰

F.解決辦法

不要在子線程中做UI操作,比如更改界面,吐司等等……

方法1:需先在該線程中手動開啟Looper(Looper.prepare()-->Looper.loop()),然后將其綁定到Handler對象上;

final Runnable runnable = new Runnable() {
  @Override
  public void run() {
    //執行耗時操作
    try {

      Log.e("bm", "runnable線程: " + Thread.currentThread().getId()+ " name:" + Thread.currentThread().getName());

      Thread.sleep(2000);
      Log.e("bm", "執行完耗時操作了~");
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
  }
};
new Thread() {
  public void run() {
    Looper.prepare();
    new Handler().post(runnable);//在子線程中直接去new 一個handler
    Looper.loop();    //這種情況下,Runnable對象是運行在子線程中的,可以進行聯網操作,但是不能更新UI
  }
}.start();

方法2:通過Looper.getMainLooper(),獲得主線程的Looper,將其綁定到此Handler對象上。

final Runnable runnable = new Runnable() {
  @Override
  public void run() {
    //執行耗時操作
    try {
      Log.e("bm", "runnable線程: " + Thread.currentThread().getId()+ " name:" + Thread.currentThread().getName());
      Thread.sleep(2000);
      Log.e("bm", "執行完耗時操作了~");
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
  }
};
new Thread() {
  public void run() {
      //在子線程中直接去new 一個handler
    new Handler(Looper.getMainLooper()).post(runnable);
    //這種情況下,Runnable對象是運行在主線程中的,不可以進行聯網操作,但是可以更新UI
  }
}.start();

G.其他延申

3.2 platform-toolsadb.exe,start-server" failed -- run manually if necessary

A.詳細崩潰日志信息

B.查看崩潰類信息

C.項目中異常分析

adb啟動失敗,端口被占用

D.引發崩潰日志的流程分析

F.解決辦法

百度google大家多說的是任務管理器 kill掉adb 或者重啟adb server,但我任務管理器就沒有adb ,猜測是某個程序占用了adb端口。于是按此思路查找。
5037為adb默認端口 查看該端口情況如下:
netstat -aon|findstr "5037"
TCP 127.0.0.1:5037 0.0.0.0:0 LISTENING 6540
發現6540占用了 5037端口,繼續查看6540的task,發現是wandoujia .如下所示
tasklist|findstr "6540"
wandoujia_daemon.exe 6540 Console 1 4,276 K

接下來問題就好解決了,在任務管理器kill掉wandoujia_daemon.exe ,運行android程序,ok .

1.關閉xx莢進程
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.詳細崩潰日志信息

非法參數,開始讀取時應該是{}括號,所以需要處理String字符串,它有可能不是標準的json數據

java.lang.IllegalStateException: ExpectedBEGIN_OBJECT but was STRING at line 1 column 1 path $

B.查看崩潰類信息

C.項目中異常分析

Gson解析數據出現問題,原因服務器返回數據不嚴謹

D.引發崩潰日志的流程分析

可能的錯誤:

bean類字段類型和字段名稱不一致。

服務器訪問得到的字符串不是純json前面有空格和回車等字符(難發現)。

如果訪問的json字符串不是utf-8編碼時,用Gson解析會出這種問題,在日志中打印會發現json的{}前面有亂碼字符,也需要注意一下。這是因為不同的編碼的原因導致的,因此必須訪問utf-8的json字符串,才會減少這種問題。

問題可能是:字符串并不是純json字符串,開頭可能會帶有空字符或者回車符,這屬于服務器問題,但我們也可以解決。

最重要原因的我們網絡請求后結果是字符串,而不是json,因此需要處理。

F.解決辦法

/**

*/
public static boolean isJson(String value) {
    try {
        new JSONObject(value);
    } catch (JSONException e) {
        return false;
    }
    return true;
}

/**
* 判斷是否是json結構
*/
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.其他延申,補充說明

解決辦法:后臺輸出穩定的Gson格式。此方法工程量太大

真正的問題是我的數據結構有問題

例如下面Json字符串:

{"code":1,"info":"success","results":{"id":"1","name":"hehe"}}

results對應的應該是一個實體類,如果這個時候想把他解析為String或者List就會出現異常

如果參考使用GsonForm處理后的數據模型,幾乎不會出現問題;加入result后面的內容可能在請求時會因為某些原因會存在格式上的變化,這個時候就有出現該異常的風險。Gson中,關鍵字后面出現""引起來的內容將會被只認為是STRING,“{}”只被認為是類,“[]”只被認為是List,這個幾乎是強制性的。

就是說如果你的實體預計是獲取String的變量,但是關鍵字后面對應的卻出現了“{”或“[”,那么這個轉換將被認為是錯誤的,拋出異常。

3.4 android.content.ActivityNotFoundException: No Activity found to handle Intent

A.詳細崩潰日志信息

android.content.ActivityNotFoundException: No Activity found to handle Intent

B.查看崩潰類信息

當調用{@link Context#startActivity}或其變體之一失敗時,會引發此異常,因為無法找到執行給定意圖的活動。

public class ActivityNotFoundException extends RuntimeException
{
    public ActivityNotFoundException()
    {
    }

    public ActivityNotFoundException(String name)
    {
        super(name);
    }
};

C.項目中異常分析

D.引發崩潰日志的流程分析

F.解決辦法

第一種辦法:做一個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();
}

第二種辦法:判斷是否有應用寶客戶端

//避免安裝了應用寶的用戶點擊其他外部鏈接走此方法導致崩潰
//判斷是否用應用寶客戶端
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導致崩潰

A.詳細崩潰日志信息

出錯代碼位置
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.項目中異常分析

D.引發崩潰日志的流程分析

原因分析(Binder造成)

如果一個進程中使用的Binder內容超過了1M,就會crash.

如果Binder的使用超出了一個進程的限制就會拋出TransactionTooLargeException這個異常。

如果是其他原因造成Binder crash的話就會拋出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() {
        //這個Demo就是同時創建兩個線程來進行Binder調用.
        for (int i = 0; i < 2; i++) {
            new Thread() {
                @Override
                public void run() {
                    int count = 0;
                    List list = 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();
        }
    }
}

錯誤打印日志

解決方式:其實只要避免多個線程同時來調用Binder就可以了,畢竟一個線程用了會釋放,所以理論上是很難發生的。

synchronized(MainActivity.class){ 
    PackageInfo pi = getPackageManager() .getPackageInfo(info.packageName, PackageManager.GET_ACTIVITIES); 
} 

3.6 IllegalArgumentException View添加窗口錯誤

A.詳細崩潰日志信息

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.項目中異常分析

該異常表示view沒有添加到窗口管理器,通常是我們dismiss對話框的時候,activity已經不存在了,建議不要在非UI線程操作對話框。

D.引發崩潰日志的流程分析

常發生這類Exception的情形都是,有一個費時的線程操作,需要顯示一個Dialog,在任務開始的時候顯示一個對話框,然后當任務完成了在Dismiss對話框,如果在此期間如果Activity因為某種原因被殺掉且又重新啟動了,那么當dialog調用dismiss的時候WindowManager檢查發現Dialog所屬的Activity已經不存在,所以會報錯。要避免此類Exception,就要正確的使用對話框,也要正確的使用線程

F.解決辦法

可以參考崩潰bug日志總結1中的1.7

G.其他延申,建議

不要在非UI線程中使用對話框創建,顯示和取消對話框;

盡量少用多帶帶線程,出發是真正的耗時操作采用線程,線程也不要直接用Java式的匿名線程,除非是那種單純的操作,操作完成不需要做其他事情的。

如果是在fragment中發起異步網絡的回調中進行dialog的操作,那么在操作之前,需要判斷 isAdd( ),避免fragment被回收了但是還要求dialog去dismiss

在Activity onDestroy中對Dialog提前進行關閉

3.7 IllegalStateException: Not allowed to start service Intent異常崩潰

A.詳細崩潰日志信息

 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.項目中異常分析

D.引發崩潰日志的流程分析

F.解決辦法

G.其他延申

3.8 java.lang.IllegalStateException:Can not perform this action after onSaveInstanceState

A.詳細崩潰日志信息

B.查看崩潰類信息

C.項目中異常分析

通過下面的源碼分析,我們可以知道,出現以上崩潰日志的原因,是因為我們在按下頁面返回鍵的時候,當前Activity以及在執行銷毀操作(也就是說我們以前在其他地方調用了finish方法)。

D.引發崩潰日志的流程分析

問題所在是Activity#onBackPressed()方法。查看源代碼:點擊onBackPressed方法中的super

在FragmentActivity中

@Override
public void onBackPressed() {
    if (!mFragments.getSupportFragmentManager().popBackStackImmediate()) {
        super.onBackPressed();
    }
}

接著再次點擊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()方法執行了兩個操作,第一個是獲取當前的FragmentManager,并且執行退棧操作,第二個是在退棧完成之后,執行finish方法。繼續查看源碼,關鍵是FragmentManager實現類的popBackStackImmediate方法

@Override
public boolean popBackStackImmediate() {
    checkStateLoss();
    executePendingTransactions();
    return popBackStackState(mHost.getHandler(), null, -1, 0);
}

我們看到,在執行退棧動作之前,這里還有一步檢查操作

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,在調用super.onBackPressed的時候,我們需要判斷當前Activity是否正在執行銷毀操作。

if (!isFinishing()) {
    super.onBackPressed();
}

方案2,通過上面的源碼分析,我們也知道了,super.onBackPressed最后也是調用finish()方法,因此我們可以重寫onBackPressed,直接調用finish方法。

G.其他延申

3.9 在Fragment中通過getActivity找不到上下文,報null導致空指針異常

A.詳細崩潰日志信息

B.查看崩潰類信息

C.項目中異常分析

使用ViewPager+Fragment進行視圖滑動,在某些部分邏輯也許我們需要利用上下文Context(例如基本的Toast),但是由于Fragment只是衣服在Activity容器的一個試圖,如果需要拿到當前的Activity的上下文Context就必須通過getActivity()獲取。

遇過出現getActivity()出現null的時候導致程序報出空指針異常。其實原因可以歸結于因為我們在

切換fragment的時候,會頻繁被crash

系統內存不足

橫豎屏幕切換的時候

以上情況都會導致Activity被系統回收,但是由于fragment的生命周期不會隨著Actiivty被回收而被回收,因此才會導致getActivity()出現null的問題。

很多人都曾被這個問題所困擾,如果app長時間在后臺運行,再次進入app的時候可能會出現crash,而且fragment會有重疊現象。如果系統內存不足、切換橫豎屏、app長時間在后臺運行,Activity都可能會被系統回收然后重建,但Fragment并不會隨著Activity的回收而被回收,創建的所有Fragment會被保存到Bundle里面,從而導致Fragment丟失對應的Activity。

D.引發崩潰日志的流程分析

當遇到getActivity()為null,或getContext()時,先冷靜想想以下3點:

1.是不是放在了第三方的回調中

2.是不是在其他進程中調用了(其實第一點就是在其他進程中調用了)

3.是不是調用時不在指定生命周期范圍內(onAttach與onDetach之間)

F.解決辦法

在Fragment中直接調用
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);
    }
    ……
}

如果從最近使用的應用里面點擊我們的應用,系統會恢復之前被回收的Activity,這個時候FragmentActivity在oncreate里面也會做Fragment的恢復

@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();
}

假設我們的頁面叫MyActivity(繼承自FragmentActivity),其中用到的Fragment叫MyFragment。出現上面這種情況時,app發生的變化如下:

1、在前面提到的幾種情況下系統回收了MyActivity

2、通過onSaveInstanceState保存MyFragment的狀態

3、用戶再次點擊進入app

4、由于MyActivity被回收,系統會重啟MyActivity,根據之前保存的MyFragment的狀態恢復fragment

5、MyActivity的代碼邏輯中,會再次創建新的MyFragment

6、頁面出現混亂,覆蓋了兩層的fragment。假如恢復的MyFragment使用到了getActivity()方法,會報空指針異常

對于上面的問題,可以考慮下面這兩種解決辦法:

1、不保存fragment的狀態:在MyActivity中重寫onSaveInstanceState方法,將super.onSaveInstanceState(outState);注釋掉,讓其不再保存Fragment的狀態,達到fragment隨MyActivity一起銷毀的目的。

2、重建時清除已經保存的fragment的狀態:在恢復Fragment之前把Bundle里面的fragment狀態數據給清除。方法如下:

if(savedInstanceState!= null){
    String FRAGMENTS_TAG =  "Android:support:fragments";
    savedInstanceState.remove(FRAGMENTS_TAG);
}

4.1 IllegalArgumentException導致崩潰【url地址傳入非法參數,轉義字符】

A.詳細崩潰日志信息

B.查看崩潰類信息

C.項目中異常分析

只有很少一部分傳入非法參數導致崩潰,不能直接用常規方法。需要過濾

D.引發崩潰日志的流程分析

F.解決辦法

Java調用 URLDecoder.decode(str,"UTF-8");拋出以上的異常,其主要原因是%在URL中是特殊字符,需要特殊轉義一下

public static String replacer(String data) {
    try {
        //使用%25替換字符串中的%號
        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/*錯誤

A.詳細崩潰日志信息

java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{*****Activity}: java.lang.ClassNotFoundException: Didn"t find class "*****Activity" on path: /data/app/*******.apk

B.查看崩潰類信息

當應用程序嘗試使用字符串名稱加載類時引發:但無法找到具有指定名稱的類的定義。從1.4版開始,已對此異常進行了修改,以符合通用的異常鏈接機制。在構建時提供并通過{@link#getException()}方法訪問的“在加載類時引發的可選異常”現在稱為原因,并且可以通過{@link Throwable#getCace()}方法以及前面提到的“遺留方法”進行訪問。

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.項目中異常分析

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/75919.html

相關文章

  • Android開發經驗實戰總結

    摘要:以前一直想寫一篇總結開發經驗的文章,估計當時的我還達不到某種水平,所以思路跟不上,下筆又捉襟見肘。在需求都還沒完成的時候把大量時間花在優化上是本末倒置的優化要用實際數據說話,借助測試工具進行檢測如網易的騰訊的和,科大訊飛的,的。 以前一直想寫一篇總結 Android 開發經驗的文章,估計當時的我還達不到某種水平,所以思路跟不上,下筆又捉襟見肘。近日,思路較為明朗,于是重新操起鍵盤開始碼...

    iflove 評論0 收藏0
  • Android性能優化之內存優化

    摘要:導語智能手機發展到今天已經有十幾個年頭,手機的軟硬件都已經發生了翻天覆地的變化,特別是陣營,從一開始的一兩百到今天動輒,內存。恰好最近做了內存優化相關的工作,這里也對內存優化相關的知識做下總結。 導語 智能手機發展到今天已經有十幾個年頭,手機的軟硬件都已經發生了翻天覆地的變化,特別是Android陣營,從一開始的一兩百M到今天動輒4G,6G內存。然而大部分的開發者觀看下自己的異常上報系...

    cheng10 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<