资讯专栏INFORMATION COLUMN

Android实际开发bug大总结

peixn / 4221人阅读

摘要:换句话说,环境或应用程序没有处于请求操作的适当状态。项目中异常分析引发崩溃日志的流程分析解决办法常见的出现场景状态异常非法线程操作。导致的方法出来显示消息位于该消息之后,迟迟没有执行。这时候,的超时检测结束,删除了服务中的记录。

目录介绍

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.项目中异常分析

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/75919.html

相关文章

  • Android开发经验实战总结

    摘要:以前一直想写一篇总结开发经验的文章,估计当时的我还达不到某种水平,所以思路跟不上,下笔又捉襟见肘。在需求都还没完成的时候把大量时间花在优化上是本末倒置的优化要用实际数据说话,借助测试工具进行检测如网易的腾讯的和,科大讯飞的,的。 以前一直想写一篇总结 Android 开发经验的文章,估计当时的我还达不到某种水平,所以思路跟不上,下笔又捉襟见肘。近日,思路较为明朗,于是重新操起键盘开始码...

    iflove 评论0 收藏0
  • Android性能优化之内存优化

    摘要:导语智能手机发展到今天已经有十几个年头,手机的软硬件都已经发生了翻天覆地的变化,特别是阵营,从一开始的一两百到今天动辄,内存。恰好最近做了内存优化相关的工作,这里也对内存优化相关的知识做下总结。 导语 智能手机发展到今天已经有十几个年头,手机的软硬件都已经发生了翻天覆地的变化,特别是Android阵营,从一开始的一两百M到今天动辄4G,6G内存。然而大部分的开发者观看下自己的异常上报系...

    cheng10 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<