Crash异常分析

空指针 — NullPointException

几个场景的避免方法:
1) 方法需要对传入的参数判空后再使用。
2) 在App中不要过多使用全局变量,因为一旦发生内存回收,这些全局变量的值都会被置为空,当使用这些变量的时候,不加判断的话,就会导致APP Crash。


角标越界 – IndexOutOfBoundsException、StringIndexOutOfBoundsException、ArrayIndexOutOfBoundsException

避免的方法:
1) 在遍历一个数组/集合时,要预判数组/集合是否为空,长度是否为0
例如:

if(mList != null && mList.size() != 0) {
       for(...){...}
}

2) 在使用数组/集合中的元素时,要预判数组/集合长度是否有这么长
例如:

if(mList != null && mList.size != 0 && index < mList.size()) {
       System.out.println("value = " + mList.get(index));
}

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

出错demo:

       TextView tv = null;
       // 尚未通过findViewById(..)获取控件实例
       Log.e(TAG, "text value = " + tv.getText().toString());

以上解决办法:就是初始化控件实例
但是有另外的出错例子,例如全局变量User类对象因为内存不够被回收,导致对象是空的,之后调用了该对象的getUserName()方法,就会导致Crash。
解决办法:在使用该对象前,对该对象进行判空操作


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

出错demo:

for(int i = 0; i < mList.size(); i++) {
    if(mList.get(i).getName().equals("test")) {
        mList.remove(i);
    }
}

移除其中元素后,其他元素的位标会发生改变。即使不会抛出ConcurrentModificationException,最后删除的结果也会跟预想的不一样。
解决办法:
1) 使用另一个List用于保存符合if语句条件的元素

ArrayList cacheList = new ArrayList();
for(int i = 0; i < mList.size(); i++) {
    if(mList.get(i).getName.equals("test")) {
        cacheList.add(mList.get(i));
    }
}
for(int i = 0; i < cacheList.size(); i++) {
    // 根据对象本身来移除元素,而不是直接根据位标来移除
    mList.remove(cacheList.get(i));
}

2) 当remove完一个,循环变量要自减1,对应变化的元素位标。

if(int i = 0; i < mList.size(); i++) {
    if(mList.get(i).getName().equals("test")) {
        mList.remove(i);
        i--;
    }
}

注:最好不要在遍历的时候删除该集合中的元素


Unable to add window – token null is not for an application,在创建一个AlertDialog时报的错误。

错误demo:

new AlertDialog.Builder(getApplicationContext())
    .setMessage("message")
    .setTitle("AlertDialog")
    .show();

问题出在AlertDialog.Buidler(getApplicationContext()),该构造函数的参数不可以是getApplicationContext()中获取的Context,而是需要Activity的Context对象,只有一个Activity才可以添加一个窗体
解决办法:

// this指Activity对象
new AlertDialog.Builder(this)
    .setMessage("message")
    .setTitle("AlertDialog")
    .show();

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

在使用child的时候,必须要先调用parent的removeView方法,解除父子关系。
给一个Activity设置界面的方法通常是:

    setContentView(R.layout.activity_main);

也可以这样

    View contentView = LayoutInflater.from(this).inflate(R.layout.activity_main,null);
    setContentView(contentView);

但是当讲contentView的的一个子View设置上去,错误就会复现。

    ViewGroup contentView = (ViewGroup)     LayoutInflater.from(this).inflate(R.layout.activity_main, null);
    View childView = contentView.findViewById(R.id.tv);
    setContentView(childView);

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.rdc.liyuzhen.myapplication/com.rdc.liyuzhen.myapplication.MainActivity}: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child’s parent first.

需要先从父布局那里remove掉,再设置上去

    ViewGroup contentView = (ViewGroup) LayoutInflater.fro          m(this).inflate(R.layout.activity_main, null);
    View childView = contentView.findViewById(R.id.tv);
    // -----------------
    contentView.removeView(childView);
    // -----------------
    setContentView(childView);

之前做项目的时候也有遇到这个情况,但是忘了在哪里出现这个问题了,到时总结的之后我再找找看。


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

只有创造视图层的原始线程在可以修改它的视图,也就是说必须要由主线程来修改UI相关内容。
解决办法:
1) 使用消息传递机制Handler
2) runOnUiThread(Runnable runnbale);
3) 使用AsyncTask
真的不能修改么?
demo:

public class MainActivity extends AppCompatActivity {
    TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tv = (TextView) findViewById(R.id.tv_hello);

        new Thread() {
            @Override
            public void run() {
                tv.setText("修改文本");
            }
        }.start();
    }
}

结果:在运行的时候并不会Crash抛异常。但在延迟一段时间修改的时候,例如休眠2000ms,Crash就会复现了。
原因:在执行onCreate()方法的时候,不能再子线程操作UI的检查机制还未起作用。
延伸:抛出异常前的流程是怎么样的?
这里写图片描述

报错原因分析之 Only the original thread that created a view hierarchy can touch its views.http://www.tuicool.com/articles/zA7jA3j


最常见的问题:android.content.res.Resources$NotFoundException:String resource ID #0x1

出错:用TextView的时候,调用方法setText();传入的参数为int类型
分析:在查看源码的时候会看到两个重载方法
1) TextView.setText(CharSequence text);
2) TextView.setText(int resId);
这时就很清楚了,当我们传入一个int值的时候,TextView并不会显示该int的值,而是跑到工程下面找到对应resource id的资源,找不到就会报错了。
解决办法:tv.setText(count + “”);


总结: 最近阅读了《App研发录》这本书,我将其中几点之前做项目时遇到的Crash问题摘录出来。希望自己在今后做项目遇到问题的时候,不要解决完问题就过了,没有去理解为什么要怎么做。就像最后一个Crash问题,setText方法放入int类型的值,该值就被当成资源id使用,在阅读这本书之前,我还不知道放入int为什么会报错,就懵懵懂懂的按着网上的步骤,在值的后面加一个< + “”>。这样的学习方式很不好,最后根本就没学到什么。所以希望自己以后遇到问题,要深入的去解决,这样才会学得更多,加油咯。

发表评论

电子邮件地址不会被公开。 必填项已用*标注