整理来源:腾讯bugly,平时遇到的问题等,本文仅供个人查阅使用并做长期更新,不作其他用途。
1.崩溃分析策略
1.1 android.view.WindowManager$BadTokenException
Unable to add window -- token android.os.BinderProxy@3d86cef3 is not valid; is your activity running?
android.view.ViewRootImpl.setView(ViewRootImpl.java:688)--操盘达人
** [解决方案]**
该异常表示不能添加窗口,通常是所要依附的view已经不存在导致的。
[解决方案]:Dialog&AlertDialog,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的情况。
1.2 java.util.concurrent.TimeoutException
android.database.BulkCursorToCursorAdaptor.finalize() timed out after 120 seconds
android.os.BinderProxy.transactNative(Native Method)--玉面小色熊
[解决方案]
该异常表示调用超时。
[解决方案]:一般是系统在gc时,调用对象的finalize超时导致,解决办法:
1.检查分析finalize的实现为什么耗时较高,修复它;
2.检查日志查看GC是否过于频繁,导致超时,减少内容开销,防止内存泄露。
1.3 java.lang.NullPointerException
[解决方案]
该异常表示尝试去调用virtual method,使用了一个空对象引用,建议您检查引用的对象是否为空。
[解决方案]:这种异常通常是调用一个对象的方法抛出的,凡是调用一个对象的方法之前,一定要进行判空或者进行try-catch,这样基本可以规避大部分空指针异常。
1.4 java.lang.NumberFormatException
[解决方案]
该异常表示字符串尝试转换为其他类型出错,转换类型异常。
[解决方案]:当字符串尝试转换为数字类型失败时,抛出该异常。举例如下:String test = "test123";int result = Integer.parseInt(test);此时由于字符串中含有非数值字段,将会抛出该异常。
1.5 #271 android.database.sqlite.SQLiteDiskIOException
[解决方案]
该异常表示磁盘输入/输出错误,可能是数据操作太频繁导致的。应用程序启动时,调用RSSDatabase的构造函数,UI Thread锁定了你的目标的数据库,随后你又尝试插入数据到被UI线程锁定的数据库。 将抛出一个异常,因为db被lock了。
1.6 java.lang.ClassCastException
[解决方案]
该异常表示类型转换异常,通常是因为一个类对象转换为其他不兼容类对象抛出的异常,检查你要转换的类对象类型。
[解决方案]:一般在强制类型转换时出现,例如如果A向B转换,而A不是B的父类时,将产生java.lang.ClassCastException异常。一般建议做这时要使用instanceof做一下类型判断,再做转换。[其他场景] :例如使用Recyclerview的itemView中的控件,设置了LayoutParams。造成转换异常。
1.7 java.lang.IllegalStateException: Fragment not attached to Activity
[原因]出现该异常,是因为Fragment的还没有Attach到Activity时,调用了如getResource()等,需要上下文Content的函数。
[解决方法] 在调用需要Context的函数之前,增加一个判断isAdded()
1.8 java.lang.ArrayIndexOutOfBoundsException
[原因]
概述:该异常表示数组越界。
[解决方案]
这种情况一般要在数组循环前做好length判断,index超出length上限和下限时都会报错。举例如下:一个数组int test[N],一共有N个元素分别是test[0]~test[N-1],如果调用test[N],将会报错。建议读取时,不要超过数组的长度(array.length)。
Android中一种常见情形就是上拉刷新中header也会作为listview的第0个位置,如果判断失误很容易造成越界。
[异常情况1]
TextView 中 ellipsize 使用引发 Crash,该问题为 Android 系统 bug,存在于 Android 5.0 及以下设备,问题描述参考:https://code.google.com/p/android/issues/detail?id=33868
[解决方案]:使用 android:singleLine="true" 代替 android:lines="1" 和 android:maxLines="1"
1.9 java.lang.ClassNotFoundException
[原因]
该异常表示在路径下,找不到指定类,通常是因为构建路径问题导致的。
[解决方案]
类名是以字符串形式标识的,可信度比较低,在调用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是你自实现的(插件化应用中常见)。
1.10 java.lang.SecurityException
Requires READ_PHONE_STATE: Neither user 1000 nor current process has android.permission.READ_PHONE_STATE.
[原因]
该异常表示需要读取电话状态,但没有权限。
[解决方案]此类问题一般是未申明权限导致,建议检查是否有读取电话状态的权限,解决方法:
1.android6.0以下需要在manifest中声明相应的权限;
2.android6.0及以上,在使用时需要动态申请权限;
1.11 android.content.ReceiverCallNotAllowedException
BroadcastReceiver components are not allowed to register to receive intents
com.bocharov.xposed.fscb.util.EventsReceiver$class.startReceive(SourceFile:46)
[解决方案]
1、bindService不能在BroadcastReceiver 中调用,你可以在里面调用StartService并把要传递参数放到intent中
2、registerReceiver不能在BroadcastReceiver调用,可以通过context.getApplicationContext().registerReceiver()
#######1.12 java.lang.IllegalArgumentException
[解决方案]
参数不匹配异常,通常由于传递了不正确的参数导致。
常见于:
- Activity、Service状态异常;
- 非法URL;
- UI线程操作。
- Fragment中嵌套了子Fragment,Fragment被销毁,而内部Fragment未被销毁,所以导致再次加载时重复,在onDestroyView() 中将内部Fragment销毁即可
1.13 android.os.TransactionTooLargeException
[原因]
Binder传输的数据太大导致的异常。
如果Binder的参数或返回值太大,不适合的事务缓冲区,然后调用将失败,并将被抛出TransactionTooLargeException。
[解决方法] 不要将大量数据传入Binder
1.14 java.lang.IncompatibleClassChangeError
[解决方案]
不兼容的类变化错误。当正在执行的方法所依赖的类定义发生了不兼容的改变时,抛出该异常。一般在修改了应用中的某些类的声明定义而没有对整个应用重新编译而直接运行的情况下,容易引发该错误
1.15 java.lang.OutOfMemoryError
[解决方案]
该异常表示未能成功分配字节内存,通常是因为内存不足导致的内存溢出。
[解决方案]:OOM就是内存溢出,即Out of Memory。也就是说内存占有量超过了VM所分配的最大。怎么解决OOM,通常OOM都发生在需要用到大量内存的情况下(创建或解析Bitmap,分配特大的数组等),这里列举常见避免OOM的几个注意点:
1.适当调整图像大小。
2.采用合适的缓存策略。
3.采用低内存占用量的编码方式,比如Bitmap.Config.ARGB_4444比Bitmap.Config.ARGB_8888更省内存。
4.及时回收Bitmap。
5.不要在循环中创建过多的本地变量。
6.自定义对内存分配大小。
7.特殊情况可在mainfests的Application中增加 android:largeHeap="true"属性,比如临时创建多个小图片(地图marker)
1.16 android.os.DeadSystemException
1.17 java.lang.RuntimeException
Can't create handler inside thread that has not called Looper.prepare()
【解决方案】
该异常表示不能在非UI线程里面创建handler对象,通常是因为在工作线程中处理UI相关的操作或者在非UI线程中new新的Handler导致;
不能在子线程里Toast等操作UI线程
[解决方案]:android中的UI操作都必须在主线程中处理的,在涉及UI操作时通常可以:
1.使用mHandler = new Handler(Looper.getMainLooper()),然后在handler中处理操作;
2.使用 Activity.runOnUiThread()方法。
1.18 java.lang.IllegalArgumentException
java.lang.IllegalArgumentException:Receiver not registered
Receiver没有注册
1.19 java.lang.IllegalStateException: Fragment not attached to Activity
isAdded()方法可以判断当前的Fragment是否已经添加到Activity中,只有当Fragment已经添加到Activity中时才执行getResources()等方法。
1.20 Caused by: java.lang.IllegalStateException: BT Adapter is not turned ON
1.21 android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)
database is locked产生的原因:sqlite同一时间只能进行一个写操作,当同时有两个写操作的时候,后执行的只能先等待,如果等待时间超过5秒,就会产生这种错误.同样一个文件正在写入,重复打开数据库操作更容易导致这种问题的发生。
1.22 java.util.ConcurrentModificationException
该异常表示迭代器迭代过程中,迭代的对象发生了改变,如数据项增加或删除。
[解决方案]:由于迭代对象不是线程安全,在迭代的过程中,会检查modCount是否和初始modCount即expectedModCount一致,如果不一致,则认为数据有变化,迭代终止并抛出异常。常出现的场景是,两个线程同时对集合进行操作,线程1对集合进行遍历,而线程2对集合进行增加、删除操作,此时将会发生ConcurrentModificationException异常。
具体方法:多线程访问时要增加同步锁,或者建议使用线程安全的集合:
- 使用ConcurrentHashMap替换HashMap,CopyOnWriteArrayList替换ArrayList;
- 或者使用Vector替换ArrayList,Vector是线程安全的。Vector的缺点:大量数据操作时,由于线程安全,性能比ArrayList低.
1.23 Caused by: kotlin.TypeCastException: null cannot be cast to non-null type
该异常表示类型转换异常,空的实例不能转化为非空类型,比如
mAccountName = view.findViewById<View>(R.id.account_name) as TextView
前面获取的实例可能为空
[解决方案]在转化的类型TextViwe后面加上?即可
1.24 Caused by: java.lang.RuntimeException: stub
该异常表示引入的库异常,比如
Caused by: java.lang.RuntimeException: stub
Telephony$Sms.<clinit>(Unknown Source:36)
这里使用的是自定义的jar包中的库文件
【解决方案】改为引用android.provider.Telephony
2.ANR分析策略
1.Subject: Input dispatching timed out (Waiting because no window has focus but there is a focused application that may eventually add a window when it finishes starting up.)
【解决方案】1.查看/data/anr/traces.txt文件,搜索包名关键字,找到打印出的堆栈信息
2.Binder超时出现的ANR
android.os.BinderProxy.transactNative(Native method)
android.os.BinderProxy.transact(Binder.java:764)
3.bindApplication超时
06-26 10:45:26.778 618 618 E ANR_LOG : >>> msg's executing time is too long
06-26 10:45:26.779 618 618 E ANR_LOG : Blocked msg = { when=-2s822ms what=100 target=android.app.ActivityThread$H obj=ActivityRecord{79acb8f token=android.os.BinderProxy@eae8a6d {com.jiayuan/com.igexin.sdk.GActivity}} } , cost = 2819 ms
06-26 10:45:26.779 618 618 E ANR_LOG : >>>Current msg List is:
OOM是常见的java错误,OOM主要有:
1.OOM fo heapjava.lang:OutOfMemoryError: Java heap space,此OOM是由于JVM中heap的最大值不满足需要,将设置heap的最大值调高即可。
2.OOM for Perm:java.lang:OutOfMemoryError: Java perm space,此OOM是由于JVM中perm的最大值不满足需要,将设置perm的最大值调高即可,参数样例为:-XX:MaxPermSize=512M
3.OOM for GC=>例如:java.lang:OutOfMemoryError: GC overhead limit exceeded,此OOM是由于JVM在GC时,对象过多,导致内存溢出,建议调整GC的策略
4.OOM for native thread created:java.lang.OutOfMemoryError: unable to create new native thread,此OOM是由于进程剩余的空间不足,导致创建进程失败
5.OOM for allocate huge array:Exception in thread "main": java.lang.OutOfMemoryError: Requested array size exceeds VM limit,此类信息表明应用程序(或者被应用程序调用的APIs)试图分配一个大于堆大小的数组
6.OOM for small swap:Exception in thread "main": java.lang.OutOfMemoryError: request <size> bytes for <reason>. Out of swap space?,抛出这类错误,是由于从native堆中分配内存失败,并且堆内存可能接近耗尽
7.OutOfMemoryError thrown while trying to throw OutOfMemoryError; no stack trace available,抛出这类错误,一般是由于方法重复调用、死循环引起,直至内存耗尽
[解决方案]The core Android system has died and is going through a runtime restart. All running apps will be promptly killed. 应用调用相关接口时,此时会抛出RuntimeException,而不会抛出DeadSystemException,此处可以应用可以捕获运行时异常RuntimeException。