您的位置:首页 > 移动开发

Android异常整理——《App研发录—架构设计,Crash分析和竞品技

2018-01-29 12:51 429 查看
常见的异常
Java语法相关的异常

空指针:NullPointException

1.方法需要对传入的参数判空后再使用
2.对外部接口的调用,需要确保返回值中不为空
3.在App中过多使用全局变量,一旦发生内存回收,全局变量会被置为空,可以将全局变量序列化到本地,为空时从本地反序列化回来

角标越界:IndexOutOfBoundsException,StringIndexOutOfBoundsException,ArrayIndexOutOfBoundsException

1.在遍历一个数组/集合时,要预判数组/集合是否为空,长度是否大于0
2.在使用数组/集合中的元素时,要预判数组/集合长度是否有这么长
3.先判断字符串长度再使用,subString(start,end)这样的函数

试图调用一个空对象的方法:Attempt to invoke virtual method on a null object reference

类型转换异常:ClassCastException:classA cannot be cast to classB

1.强制类型转换导致,可以使用安全类型转换函数,如convertToInt(obj, 0)
public final static int convertToInt(Object value, int defaultValue) { 
if (value == null || "".equals(value.toString().trim())) {
return defaultValue;
}
try {
return Integer.valueOf(value.toString()); 
} catch (Exception e) { 
try {
return Double.valueOf(value.toString()).intValue(); 
} catch (Exception e1) {
return defaultValue; 

}
}

数字转换错误:NumberFormatException

如:
String abc = "1122xx";
int result = Integer.parseInt(abc);

声明数组时长度为-1:NegativeArraySizeException

如:
String[] arg1 = new String[args.length - 1];//args.length为0

遍历集合同时删除其中元素:ConcurrentModificationException

比较器使用不当:Comparison method violates its general contract!

如:
Comparator comparator = new Comparator(){

public int compare(Double d1,Double d2){

return p1 > p2 ? 1 : -1;

}

};//没有考虑到p1==p2的情况

当除数为0:java.lang.ArithmeticException:divide by zero

不能随便使用的asList:

java.lang.UnsupportedOperationException at java.util.AbstractList.remove(AbstractList.java:144)

at java.util.AbstractList$Itr.remove(AbstractList.java:360) at

java.util.AbstractCollection.remove(AbstractCollection.java:252) at

如:

String str = "1,2,3";

List test = Arrays.asList(str.split(","));//asList()返的是Arrays$ArrayList,其没有实现add和remove方法

test.remove("1");

解决方法:将Arrays$ArrayList转换为ArrayList

String str = "1,2,3";

List test = Arrays.asList(str.split(","));//asList()返的是Arrays$ArrayList,其没有实现add和remove方法

List arrayList = new ArrayList(test);

arrayList.remove("1");

类找不到:ClassNotFoundException

动态加载一个类时,如果这个类在运行时找不到,就会抛出这个异常

类找不到:NoClassDefFoundError

当在一个B来中声明A类的实例

ClassA obj = new ClassA();

但是打包时B和A在不同的dex中,如果在A所在的dex中把A类删除了,就会抛出此异常

Activity相关的异常

找不到Activity:android.content.ActivityNotFoundException:No Activity found to handle Intent {…}

错误原因:URL不是以http开头,代码就会抛出异常

Uri uri = Uri.parse("www.baidu.com");

Intent intent = new Intent(Intent.ACTION_VIEW,uri);

startActivity(intent);

不能实例化Activity:java.lang.RuntimeException:Unable to instantiate activity ComponentInfo

没有在清单文件中注册

找不到Service:java.lang.RuntimeException:Unable to instantiate receiver

注意检查代码中是否有Class.forName("class1")这样的语句,ProGuard会将class1混淆,从而就找不到class1这个类

不能启动BroadcastReceiver:Unable to start receiver

使用Activity以外的content来startActivity,必须指定为Intent.FLAG_ACTIVITY_NEW_TASK

intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

startActivityForResult不能回传:Failure delivering result ResultInfo{who=null,request=0,result=-1}

传回来的key是A,但是却按照B这个key来取值

Fragment not attached to Activity

Fragment还没有Attach到Activity时就调用了诸如getResourse()这样的方法

解决方案:先使用isAdded方法进行判断

if(isAdded()){//isAdded方法是Android系统提供的,只有在Fragment被添加到所属的Activity后才返回true

getResourses().getString(...);

}

序列化相关的异常

实体对象不支持序列化:Parcelable encountered IOException writing serializable object(name=xxx)…

序列化时未指定ClassLoader:BadParcelableException:ClassNotFoundException when unmarshalling…

public class MyParcelable implements Parcelable {

private String mStr;

private ClassA a;

...

private MyParcelable(Parcel in) {

mStr = in.readString();

a = in.readParcelable(null);

}



崩溃处在最后一句,对a的反序列化上,改成

a = in.readParcelable(ClassA.class.getClassLoader());

反序列化时发现类找不到(被ProGuard混淆导致的崩溃):Parcelable encountered ClassNotFoundException reading a Serializable object …

-keep class * implements java.io.Serializable

-keepclassmembers class * implements java.io.Serializable {

static final long serialVersionUID;

private static final java.io.ObjectStreamField[] serialPersistentFields;

!static !transient ;

!private ;

!private ;

private void writeObject(java.io.ObjectOutputStream);

private void readObject(java.io.ObjectInputStream);

java.lang.Object writeReplace();

java.lang.Object readResolve();

}

反序列化时发现类找不到(传入畸形数据):parcelable encountered ClassNotFoundException reading a Serializable object(name=某个类的名称)

反序列化时出错:Could not read input channel file descriptors from parcel…

可能是intent传递的数据太大导致的

列表相关的异常

Adapter数据源变化但是没通知ListView:The content of the adapter has changed but ListView did not receive a notification…

ListView滚动时点击刷新按钮后崩溃:

java.lang.IndexOutOfBoundsException:Invalid index 30,size is 1 at 

java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:251)…

解决方案:ListView滚动的时候将刷新按钮设置为不可点击

AbsListView的obtainView返回空指针:java.lang.NullPointerException at android.widget.AbsListView.obtain View(AbsListView.java:1521)at android.widget.ListView.makeAndAddView

导致的原因是getView方法在某些时候返回null,返回的时候需判空,如果为null就返回convertView

Adapter数据源变化但是没调用notifyDataSetChanged:The application’s PagerAdapter changed the adapter’s contents without calling PagerAdapter#notifyDataSetChanged

PagerAdapter对于notifyDataSetChanged()和getCount()的执行顺序是非常严格的,系统跟踪count的值,如果这个值和getCount返回的值不一致,就会抛出此异常

窗体相关的异常

窗体句柄泄露:

android.view.WindowLeaded:Activity xxx has leaked window

com.android.internal.policy.impl.Phone.Window$DecorView{xxxx} that was originally added here.

可能是在非主线程中的某些操作不当而产生了一个严重的异常,从而强制关闭当前Activity,而在关闭的同时,没能及时调用dismiss来解除ProgressDialog等的引用,从而系统抛出上述崩溃信息

解决方法:重写Activity的onDestroy方法,在方法中调用dismiss来解除对ProgressDialog等的引用

View not attached to window manager:

java.lang.IllegalArgumentException:View not attached to window manager at

android.view.WindowManagerImpl.findViewLocked(WindowManagerImpl.java:356) at

android.view.WindowManagerImpl.removeView(WindowManagerImpl.java:201) at…

发生这类Exception的场景是,有一个耗时的线程任务,在任务开始的时候显示一个对话框,然后当任务完成后再销毁,在此期间Activity因为某种原因被杀掉且又重新启动了,那么当Dialog调用dismiss方法的时候就会抛此异常

token null is not for an application:android.view.WindowManager$BadTokenException:Unable to add window – token null is not for an application

new AlertDialog.Builder(getApplicationContext())

.setIcon(...)

...

.show();

只有Activity才能添加一个窗体

permission denied for this window type:Android.view.WindowManager$BadTokenException:Unable to add window android.view.ViewRootImpl$W@411da608 – permission denied for this window type

多发生在使用WindowManager自定义弹出框时,没有设置权限,需添加

is your activity running:

android.view.WindowManager$BadTokenException:Unable to add window – token

android.app.LocalActivityManager$LocalActivityRecord@45a58ee0 is not valid;is your activity running?

由于Activity A依附于ActivityB,当Activity B产生错误的时候,Activity A因为调用了一个已经被finish()/或者还没有执行完onCreate()的Activity抛出此异常

添加窗体失败

java.lang.RuntimeException:Adding window failed at

android.view.ViewRootImpl.setView(ViewRootImpl.java:511) at 

android.view.WindowManagerImpl.addView(WindowManagerImpl.java:301) at 

android.view.WindowManagerImpl.addView(WindowManagerImpl.java:215) at…

AlertDialog.resolveDialogTheme

java.lang.NullPointerException at 

android.app.AlertDialog.resolveDialogTheme(AlertDialog.java:142) at 

android.app.AlertDialog$Builder.(AlertDialog.java:359) at 

com.radzik.devadmin.MainActivity$5.onClick(MainActivity.java:140) at 

android.view.View.performClick(View.java:4084)…

1.在B页面写了一个show方法,控制AlertDialog.Builder的弹出和隐藏,在A页面调用B页面的show方法

2.在TabActivity中切换Tab时,也容易产生这个Crash,因为在new对话框的时候参数content指定成了this,即指向当前子Activity的content,但子Activity是动态创建的,不能保证一直存在,所以将this替换为getParent()即可

The specified child already has a parent.You must call removeView() on the child’s parent first

先将child从它的父布局中移除:parent.removeView(child);

再设置

子线程不能修改UI:android.view.ViewRootImpl$CalledFromWrongThreadException:Only the original thread that created a view hierarchy can touch its views…

不能在子线程操作AlertDialog和Toast:Can’t create handler inside thread that has not called Looper.prepare()

资源相关的异常

Resources$NotFoundException:android.content.res.Resources$NotFoundException:String resource ID #0x1

StackOverfiowError

主要是因为Layout布局文件结构嵌套层次太深,尽量控制在5层以下

还有可能是App中有多个线程,在退出App的时候可能不能完全关闭App,必须使用System.exit(0)

UnsatisfiedLinkError: 

java.lang.UnsatisfiedLinkError:dalvik.system.PathClassLoader [DexPathList[[zip file “/data/app/appname-1.apk”]…

造成这个Crash肯定是so格式的文件没有加载到

InfiateException之FileNotFoundException

Caused by:android.view.InfiateException:Binary XML file line #18:Error infiating class at

android.view.LayoutInfiater.createView(LayoutInfiater.java:518) 

Caused by:java.io.FileNotFoundException:res/drawable-hdpi/add.png at

android.content.res.AssetManager.openNonAssetNative(Native Method)

可能是GC导致的,Activity销毁了,但是里面涉及的资源并没有被回收,于是便产生内存泄露,但是表现为FileNotFoundException

解决方案是在Activity的onStop方法中,手动释放每一张图片资源:

Drawable d = imageView.getDrawable();

if (d != null)

d.setCallback(null);

imageView.setImageDrawable(null);

imageView.setBackgroundDrawable(null);

InfiateException之缺少构造器:

android.view.InfiateException:Binary XML file line #:Error infiating class com.example.activity1.TestButton

自定义控件若需在xml文件中使用,就必须重写带有两个参数的构造方法

InfiateException之style与android:textStyle的区别:android.view.InfiateException:Binary XML file line #14:Error infiating class

引用定义好的样式

style = "@style/NormalText"

TransactionTooLargeException:android.view.InfiateException:Binary XML file line #14:Error infiating class

Binder最大通常限制为1MB,大于1MB就会抛出此异常

系统碎片化相关的异常

NoSuchMethodError:java.lang.NoSuchMethodError

可能是Android不同版本的API不同导致,可以在不同版本的SDK上编译,发现错误,或者对版本进行判断

RemoteViews:android.widget.RemoteViews$RefiectionAction.writeToParcel(RemoteViews.java:763)

当Android版本是4.1以下时,Bitmap为null会抛出此异常

pointerIndex out of range:

java.lang.IllegalArgumentException:pointerIndex out of range at

android.view.MotionEvent.nativeGetAxisValue(Native Method)

在做多点触控放大缩小,操作自己绘制的图形时发生此异常,可以在绘图的时候捕获这个异常

SecurityException之一:Intent中图片太大

Unable to find app for caller android.app.ApplicationThreadProxy@41868f10(pid=24370) when stopping service Intent {cmp=xxx}

SecurityException之二:动态加载其他apk的activity

java.lang.SecurityException:Given caller package com.jianqiang.abc is not running in process ProcessRecord {41e74e50 28637:com.zhao3546.launcher/u0a10142}

如果在apk中使用了动态注册BroadcastReceiver,那么Launcher动态加载该apk时,就有可能出现java.lang.SecurityException异常

解决方法参照:http://blog.csdn.net/zhao_3546/article/details/11195881

SecurityException之三:No permission to modify thread

java.lang.SecurityException:No permission to modify given thread at

android.os.Process.setThreadPriority(Native Method) at

android.webkit.WebViewCore$WebCoreThread$1.handleMessage(WebViewCore.java:764)

在执行某些需要权限的操作时,要么加上if语句跳过这个操作,要么使用try...catch...捕获这类异常

view的getDrawingCache()返回null

java.lang.NullPointerException at

android.view.View.buildDrawingCache(View.java:6578) at

android.view.View.getDrawingCache(View.java:6428) at…

当背景图太大,超过了屏幕大小,就会导致getDrawingCache()返回的结果是null

DeadObjectException

某个对象已经被系统回收了,可我们还在使用它

ViewFlipper引发的血案

java.lang.IllegalArgumentException:Receiver not registered: 

android.widget.ViewFlipper$1@4083a4d0 at 

android.app.LoadedApk.forgetReceiverDispatcher(LoadedApk.java:634)

在Activity中使用ViewFlipper控件,进行横竖屏切换操作时就会发生这种异常,这是由于onDetachedFromWindow()在onAttachedToWindow()之前被调用所致,需重写onDetachedFromWindow()方法

@Override

protected void onDetachedFromWindow(){

try{

super.onDetachedFromWindow();

}catch(IllegalArgumentException e){

stopFlipping();

}

}

ActivityNotFoundException:

android.content.ActivityNotFoundException:Unable to find explicit activity class

{com.android.settings/com.android.settings.WirelessSettings}; 

have you declared this activity in your AndroidManifest.xml?

Android4.0以上把原先的打开网络设置方式舍弃了

if(Build.VERSION.SDK_INT > 13){

startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS));

}else {

startActivity(new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS));

}

Package manager has died:

Packeage manager has died at 

android.app.ApplicationPackageManager.getApplicationInfo(ApplicationPackageManager.java:213)

try{

String channelId = getPackageManager()

.getApplicationInfo(

getPackageName(),PackageManager.GET_META_DATA)

.metaData.getString("UMENG_CHANNEL");

PackageInfo info = this.getPackageManager()

.getPackageInfo(getPackageName(),0);

}catch(PackageManager.NameNotFoundException e){

}

PackageManager如果已经died,说明该进程不存在了,此时任何向它进行的请求都将失效,解决方案就是每次获取PackageManager的时候用try...catch...捕获异常

SpannableString与富文本字符串:java.lang.IndexOutOfBoundsException:setSpan(-1…-1) starts before 0 at android.text.SpannableStringBuilder.checkRange(SpannableStringBuilder.java:951) at …

可能是在长按一段文本时,有些Android系统对于EditText的getSelectionStart方法,会返回-1

Can not perform this action after onSaveInstanceState

java.lang.IllegalStateException: 

Can not perform this action after onSaveInstanceState at 

android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1314)… 

android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:595)

commit()方法在Activity的onSavaInstanceState()之后调用就会出错,因为onSaveInstanceState方法是在Activity即将被销毁前调用,以保存Activity数据的,如果在保存完状态后再添加Fragment就会出错

解决方法是把commit()方法替换成commitAllowingStateLoss()

Service Intent must be explicit

Android在升级到5.0系统后会产生这样的崩溃,直接通过action启动Service就会导致这个问题,必须指定component或package

Intent intent= new Intent();

intent.setAction("your action name");

intent.setPackage(getPackageName());

context.startService(intent);

SQLite相关的异常

No transaction is active:android.database.sqlite.SQLiteException:cannot commit - no transaction is active

在事务中,逐条循环插入大量数据时会导致此崩溃,解决方法是一次性地把这些数据都插入到数据库中

public void insertOrUpdateDataBatch(){

SQLiteDatabase db = getWritableDatabase();

db.beginTransaction();

try{

for(String sql : sqls){

db.execSQL(sql);

}

//设置事务标志为成功,当结束事务时就会提交事务

db.setTransactionSuccessful();

}catch(Exception e){

e.printStackTrace();

}finally{

db.endTransaction();

db.close();

}

}

忘记关闭Cursor:android.database.CursorWindowAllocationException:Cursor window allocation of 2048kb failed

数据库被锁定:android.database.sqlite.SQLiteDatabaseLockedException:database is locked

在不同的线程中创建多个连接时,就会抛出此异常,解决方案是将数据库做成一个单例

试图再打开已经关闭的对象:java.lang.IllegalStateException:attempt to re-open an already-closed object

多线程操作数据,A读完数据库将其关闭,B此时正在写数据,就会发生此Crash

在聊天室中,保持数据库一直处于Open状态,等退出聊天室再执行close方法

文件加密了或无数据库:SQLiteDatabaseCorruptException:file is encrypted or is not a database

如果有两个不同版本的DB就会出现此异常,如果DB破损(多次插拔SD卡,导致部分文件破损),也有可能出现此异常

WebView中SQLite缓存导致的崩溃:

SQLiteDiskIOException:disk I/O error…at android.webkit.WebViewDatabase$1.run(WebViewDatabase.java:1000)

WebView中存在着两种缓存:网页数据缓存(存储打开过的页面及资源)和Html5缓存(appcache)

WebView自带的缓存机制里,会将url保存在webviewCashe.db中,将url内容保存在webviewCashe文件夹下,而对于database目录下的webview.db和webviewCashe.db都会自动生成1个名为android_metadata的表,只要创建SQLite数据库中的表,就会自动创建这个表

建议缓存策略为:判断是否有网络,有的话,使用LOAD_DEFAULT,无网络时,使用LOAD_CACHE_ELSE_NETWORK

磁盘读写错误:android.database.sqlite.SQLiteDiskIOException:disk I/O error(code 1802)

dbHelper只有在创建数据库、进行事务处理时才会锁住数据库,默认情况下,dbHelper会缓存DB实例,执行类似于getWritableDatabase的操作是立即返回的,并不会上锁

disk I/O error这类异常的抛出是因为多线程修改DB

android_metadata表不存在:

android.database.sqlite.SQLiteException:no such table:android_metadata SQLiteOpenHelper.getReadableDatabase

开发中需要连接SQLite数据库,当使用如下方法打开数据库时就会抛出上述错误:

SQLiteDatabase database = SQLiteDatabase.openDatabase(PATH,null,SQLiteDatabase.OPEN_READONLY);

解决方法是将openDatabase方法中的最后一个参数改为SQLiteDatabase.NO_LOCALIZED_COLLATORS

android_metadata表中的locale字段:android.database.sqlite.SQLiteException:Failed to change locale for db ‘/data/data/appname/databases/webview.db’ to ‘zh_CN’

数据库或磁盘满了:android.database.sqlite.SQLiteFullException:database or disk is full

内存溢出:OutOfMemoryException

在AndroidManifest.xml中有个参数可以设置:

<application android:largelheap="true"

这样就能增加系统为当前App分配的内存,但是当内存很大时,每次GC的时间也会长一些,性能就会下降

Verify Failed:java.lang.VerifyError:Rejecting class xxx.package.activityA that attempts to sub-class erroneous class xxx.package.Activity 基类

其他情况的异常

TimeoutException:com.android.internal.BinderInternal$GcWatcher.finalize() timed out after 10 seconds

GC回收超时会抛出此异常,注意重写finalize方法时不要有超时的操作

JSON解析异常:org.json.JSONException:No value for UserName at org.json.JSONObject.get(JSONObject.java:354) at…

在解析JSON的时候,使用了getString("UserName"),如果不存在此key就会抛出上述异常,可以使用optString("UserName")、optJsonArray方法

JSONArray在初始化时为空

java.lang.NullPointerException at

org.json.JSONTokener.nextCleanInternal(JSONTokener.java:116) at

org.json.JSONTokener.nextValue(JSONTokener.java:94)t…

两个不同类型的View有相同的id

java.lang.IllegalArgumentException:Wrong state class,expecting View State but received class android.widget.ScrollView$SavedState instead.This usually happens when two views of different type have the same id in the same hierarchy.This view’s id is id/0xff0000.Make
sure other views do not use the same id.

建议最好保证每个View的id都是唯一的,至少在一个布局文件中是唯一的

LayoutInfiater.from().infiate()使用不当导致的崩溃:No package identifier when getting value for resource number 0x00000001

在程序中使用LayoutInfiater.from().infiate()语句时,必须写在具体的子类中,一定不能工作在父类或虚类里

可以参考:http://blog.csdn.net/yanzi1225627/article/details/37338565

ViewGroup中的玄机:java.lang.IllegalArgumentException:parameter must be a descendant of this view

这个崩溃是ViewGroup的offsetRectBetweenParentAndChild方法抛出来的

可以参考:http://blog.sina.com.cn/s/blog_5704bfaf0102v3bn.html

图片缩放很多倍:java.lang.IllegalArgumentException:bitmap size exceeds 32bits

当图片缩放了很多倍时,导致内存溢出,就会抛出此异常,多发生在全屏显示一张图片的时候

图片宽高为0:

java.lang.IllegalArgumentException:width and height must be>0 at android.graphics.Bitmap.nativeCreate(Native Method)

通常是因为没有取到图片的宽和高(缓存数据被清空或提前调用了获取图片宽高的方法),返回默认值0抛出此异常

不能重复添加组件:View xxx has already been added to the window manager

解决方法:

try{

windowmanager.removeView(view);

}catch(IllegalStateException e){

e.printStackTrace();

}

try{

windowmanager.addView(view);

}catch(IllegalStateException e){

e.printStackTrace();

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: